Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/workflows/azure-static-web-apps.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Deploy to Azure Static Web Apps

# Hosts the site on Azure Static Web Apps with the REPO ROOT as the web root,
# so /site/* serves the app and /phases/* serves lesson content (docs/en.md,
# quiz.json) that lesson.html loads at runtime via relative paths — no GitHub
# fetch needed. Requires the repo secret AZURE_STATIC_WEB_APPS_API_TOKEN
# (the deployment token from the Azure Static Web App resource).

on:
push:
branches: [main]
pull_request:
types: [opened, synchronize, reopened, closed]
branches: [main]

permissions:
contents: read
pull-requests: write

jobs:
build_and_deploy:
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
runs-on: ubuntu-latest
name: Build and Deploy
steps:
- uses: actions/checkout@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch the commit SHA each tag currently points to

echo "actions/checkout@v4:"
gh api repos/actions/checkout/git/ref/tags/v4 --jq '.object.sha'

echo "actions/setup-node@v4:"
gh api repos/actions/setup-node/git/ref/tags/v4 --jq '.object.sha'

echo "Azure/static-web-apps-deploy@v1:"
gh api repos/Azure/static-web-apps-deploy/git/ref/tags/v1 --jq '.object.sha'

Repository: rohitg00/ai-engineering-from-scratch

Length of output: 280


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Print the relevant portion of the workflow around the referenced lines
FILE=".github/workflows/azure-static-web-apps.yml"
nl -ba "$FILE" | sed -n '1,140p'

Repository: rohitg00/ai-engineering-from-scratch

Length of output: 123


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/azure-static-web-apps.yml"
# show first 140 lines with line numbers
cat -n "$FILE" | sed -n '1,140p'

Repository: rohitg00/ai-engineering-from-scratch

Length of output: 2269


Pin GitHub Actions in .github/workflows/azure-static-web-apps.yml to immutable commit SHAs.

Mutable tags like @v4/@v1 can change the workflow code without modifying the workflow file. Replace the following uses: entries (keep the existing with: blocks unchanged):

  • Line 26: actions/checkout@v4actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
  • Line 30: actions/setup-node@v4actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
  • Line 38: Azure/static-web-apps-deploy@v1Azure/static-web-apps-deploy@1a947af9992250f3bc2e68ad0754c0b0c11566c9 # v1
  • Line 54: Azure/static-web-apps-deploy@v1Azure/static-web-apps-deploy@1a947af9992250f3bc2e68ad0754c0b0c11566c9 # v1
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 26-28: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 26-26: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/azure-static-web-apps.yml at line 26, The workflow uses
mutable GitHub Action tags; update the three `uses:` entries in
.github/workflows/azure-static-web-apps.yml to pinned immutable commit SHAs:
replace `actions/checkout@v4` with
`actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5  # v4`, replace
`actions/setup-node@v4` with
`actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020  # v4`, and replace
both occurrences of `Azure/static-web-apps-deploy@v1` with
`Azure/static-web-apps-deploy@1a947af9992250f3bc2e68ad0754c0b0c11566c9  # v1`,
keeping all existing `with:` blocks unchanged.

Source: Linters/SAST tools

with:
submodules: false
Comment on lines +26 to +28

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Set persist-credentials: false to prevent credential leakage.

The checkout action leaves GITHUB_TOKEN credentials in .git/config by default. If the build script (site/build.js) or any of its dependencies were compromised, those credentials could be read and exfiltrated.

Since this workflow does not perform any git operations after checkout, the credentials are unnecessary.

🔒 Proposed fix
       - uses: actions/checkout@v4
         with:
+          persist-credentials: false
           submodules: false
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 26-28: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 26-26: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/azure-static-web-apps.yml around lines 26 - 28, The
checkout step using actions/checkout@v4 currently leaves GITHUB_TOKEN
credentials in .git/config; update the checkout action configuration
(actions/checkout@v4) to include persist-credentials: false under the with block
so credentials are not written to the repo after checkout, since no further git
operations are needed in the workflow.

Source: Linters/SAST tools


- uses: actions/setup-node@v4
with:
node-version: 20

- name: Generate site/data.js
run: node site/build.js

- name: Deploy
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
repo_token: ${{ secrets.GITHUB_TOKEN }}
action: upload
# Repo root is the web root so /phases/* is served alongside /site/*.
app_location: "/"
output_location: ""
skip_app_build: true

close_pull_request:
if: github.event_name == 'pull_request' && github.event.action == 'closed'
runs-on: ubuntu-latest
name: Close PR preview
steps:
- name: Close
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
action: close
19 changes: 2 additions & 17 deletions site/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,27 +44,12 @@
}

function load() {
// No runtime network calls: only paint a previously cached value.
// The live GitHub fetch was removed to keep pages fully self-contained.
var cached = readCache();
if (cached != null) {
paint(cached);
return;
}
fetch('https://api.github.com/repos/' + REPO, {
headers: { Accept: 'application/vnd.github+json' },
})
.then(function (r) {
if (!r.ok) throw new Error('gh ' + r.status);
return r.json();
})
.then(function (data) {
var n = data.stargazers_count;
if (typeof n !== 'number') return;
writeCache(n);
paint(n);
})
.catch(function () {
// Leave the placeholder; the link still works.
});
}

if (document.readyState === 'loading') {
Expand Down
8 changes: 6 additions & 2 deletions site/lesson.html
Original file line number Diff line number Diff line change
Expand Up @@ -1838,8 +1838,12 @@
}

function fetchLesson(path) {
var rawUrl = 'https://raw.githubusercontent.com/rohitg00/ai-engineering-from-scratch/main/' + path + '/docs/en.md';
var quizUrl = 'https://raw.githubusercontent.com/rohitg00/ai-engineering-from-scratch/main/' + path + '/quiz.json';
// Load lesson content from the repo's local files (served relative to
// /site), not from GitHub. Requires serving the repo root over a local
// dev server (e.g. `python3 -m http.server` in the repo root) so the
// page is reached at /site/lesson.html and ../<path> resolves on disk.
var rawUrl = '../' + path + '/docs/en.md';
var quizUrl = '../' + path + '/quiz.json';

fetch(rawUrl)
.then(function (res) {
Expand Down
24 changes: 24 additions & 0 deletions staticwebapp.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"routes": [
{ "route": "/", "rewrite": "/site/index.html" },
{ "route": "/glossary", "rewrite": "/site/glossary.html" },
{ "route": "/catalog", "rewrite": "/site/catalog.html" },
{ "route": "/path", "rewrite": "/site/prereqs.html" },
{ "route": "/roadmap", "rewrite": "/site/prereqs.html" },
{ "route": "/about", "rewrite": "/site/about.html" }
],
"navigationFallback": {
"rewrite": "/site/index.html",
"exclude": ["/site/*", "/phases/*", "/assets/*", "/*.{js,css,png,jpg,jpeg,svg,webp,woff2,woff,ttf,ico,md,json}"]
},
"globalHeaders": {
"X-Content-Type-Options": "nosniff"
},
"mimeTypes": {
".md": "text/markdown",
".json": "application/json"
},
"responseOverrides": {
"404": { "rewrite": "/site/index.html" }
}
}