Skip to content

CI: guard against reading data-lectures over the LFS media host - #58

Merged
mmcky merged 2 commits into
mainfrom
ci/data-url-guard
Aug 10, 2026
Merged

CI: guard against reading data-lectures over the LFS media host#58
mmcky merged 2 commits into
mainfrom
ci/data-url-guard

Conversation

@mmcky

@mmcky mmcky commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Gate 2 of QuantEcon/workspace-lectures#23 step 3, the lecture-wasm half. One new workflow, no lecture changes. Companion to QuantEcon/lecture-python-intro#830.

The failure it catches

media.githubusercontent.com is the LFS media endpoint. It routes per path, not per repo — it serves a file only where that path is LFS-tracked in the repo the URL names, and 404s otherwise. Everything QuantEcon/data-lectures publishes is plain git, so the media host never resolves for it:

URL Result
raw.githubusercontent.com/QuantEcon/data-lectures/main/lectures/mpd2020.xlsx 200
media.githubusercontent.com/media/QuantEcon/data-lectures/main/lectures/mpd2020.xlsx 404

Both hosts send access-control-allow-origin: *, so this is host routing, not CORS — a different failure from repoint rule 5, and one that hits every runtime rather than only the browser.

It is live risk here specifically: the six high_dim_data datasets are LFS-tracked there, so heavy_tails, inequality and mle read them from the media host today. When they land in data-lectures as plain git, a mechanical org/repo swap that preserves the host breaks every one of them — in the reader's browser, on every page view.

Why this repo needs it more than intro does

ci.yml runs myst build --html with no execution, so no URL in a code cell is ever fetched by CI. A dead read is invisible here until a reader arrives. (Intro's build does execute and raises CellExecutionError on a 404 — a signal this repo does not have at all.)

grep -r over lectures/ also walks lectures/_static/**/*.ipynb, which the data-lectures audit deliberately excludes from its scan — so this guard is the only mechanism of any kind that sees _static/lecture_specific/inequality/data.ipynb.

Scope

Media host only. Rule 5's github.com/*/raw/ form is wrong in a code cell but correct in a {download} role or a prose link, and this repo has both today — french_rev.md links three blob/ URLs and inflation_history.md has a {download} on the raw redirect form. A grep cannot tell a code cell from a prose link, so rule 5 stays where it already is: asserted on code cells only, post-merge, by the strict audit in data-lectures.

Matches zero lines today — verified, grep exits 1 — so it goes green on merge and is armed before the fold rather than landing alongside it.

It is an alarm, not a gate. main here has no branch protection object at all, so this shows as a failed check on the PR without blocking anything. Making it binding is a separate, deliberate decision.

The companion assertion — that no high_dim_data URL remains anywhere — matches 7 lines here today, so it belongs in the repoint PR itself.

🤖 Generated with Claude Code

Code cells here execute in the reader's browser, so a dataset read on the
wrong host is a reader-visible failure on every page view. This repo's CI
cannot see it: ci.yml runs `myst build --html` with no execution, so nothing
fetches the URL until a reader does. The data-audit dashboard that would
catch it lives in QuantEcon/data-lectures and never runs on a pull request
here — its detection lag is up to seven days.

media.githubusercontent.com is the LFS *media* endpoint and routes per path,
serving a file only where that path is LFS-tracked in the repo the URL names.
Everything data-lectures publishes is plain git, so that host never resolves
for it — and the six datasets folded in from high_dim_data are read from it
here today, in heavy_tails, inequality and mle.

Only the media host is checked. Rule 5's github.com/*/raw/ form is wrong in a
code cell but correct in a {download} role or a prose link, and this repo has
both today (french_rev, inflation_history) — a grep cannot tell them apart,
so that rule stays with the strict audit, which scans code cells only.

Matches zero lines today (verified: exit 1), so it goes green on merge and is
armed before the fold rather than alongside it.

Gate 2 of QuantEcon/workspace-lectures#23 step 3. See QuantEcon/data-lectures
PLAN.md, repoint rule 6.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 9, 2026 22:58
@netlify

netlify Bot commented Aug 9, 2026

Copy link
Copy Markdown

Deploy Preview for beautiful-dodol-cb9543 failed.

Name Link
🔨 Latest commit 65c5c0b
🔍 Latest deploy log https://app.netlify.com/projects/beautiful-dodol-cb9543/deploys/6a79398e6834390008ef8bea

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

📖 Netlify Preview Ready!

Preview URL: https://pr-58--beautiful-dodol-cb9543.netlify.app (141d42c)

✨ Browse the preview at the URL above.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a lightweight CI alarm to detect any accidental use of the Git LFS media host for QuantEcon/data-lectures URLs within lectures/, catching reader-visible runtime 404s that this repo’s current HTML-only build won’t surface.

Changes:

  • Introduces a new GitHub Actions workflow that greps lectures/ for media.githubusercontent.com/media/QuantEcon/data-lectures.
  • Emits a workflow error and fails the job if any such URL is present.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .github/workflows/data-url-guard.yml
Comment thread .github/workflows/data-url-guard.yml
Copilot review on #58.

The job greps a checkout and writes nothing, but this repo's default
workflow permission is `write`, so it was receiving a token that could
also approve pull requests. ci.yml already sets its own block.

The error message opened with a bare imperative naming the *correct*
host — "Read data-lectures over raw.githubusercontent.com" — which reads
as a description of what was found rather than as the fix. Reordered to
name the defect first. The intro twin avoids this by carrying a "never
media.githubusercontent.com" clause; that clause is deliberately absent
here, since repoint rule 5 bars the github.com/*/raw/ form for code
cells that execute in the reader's browser.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

📖 Netlify Preview Ready!

Preview URL: https://pr-58--beautiful-dodol-cb9543.netlify.app (65c5c0b)

✨ Browse the preview at the URL above.

@mmcky
mmcky merged commit 7aa38f2 into main Aug 10, 2026
2 of 6 checks passed
mmcky added a commit to QuantEcon/lecture-python-intro that referenced this pull request Aug 10, 2026
* CI: least-privilege token for the data URL guard

The job greps a checkout and writes nothing, but this repo's default
workflow permission is `write`, so it was receiving a token that could
also approve pull requests.

Raised by Copilot on the wasm twin, QuantEcon/lecture-wasm#58, which
carries the same one-line change; it left no inline comments on
#830, so this landed here unflagged.

Scoped to this workflow. cache.yml, ci.yml and collab.yml have the same
gap and are left alone — linkcheck.yml, publish.yml and
sync-translations-zh-cn.yml already scope theirs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants