Skip to content

fix: only an http or https package address reaches src, parsed - #72

Merged
alekswebnet merged 1 commit into
mainfrom
fix/package-url-scheme
Oct 23, 2026
Merged

alekswebnet merged 1 commit into
mainfrom
fix/package-url-scheme

Conversation

@alekswebnet

Copy link
Copy Markdown
Contributor

Closes #71.

Every place that sets the element's src from a link, a text field or the cmi5 launch now resolves the address with new URL, accepts only http: and https:, and sets the parsed URL's href. Anything else is refused with a message and plays nothing. This is the fix CodeQL suggested on alert 35, applied to each place. The preview page gets the same in #70.

Changes

  • Demo pages (apps/demo/demo/page-common.js): packageUrl(src), beside foreignHost, returns the parsed URL or null.
    • The player page (the field, the samples, ?src=), the cmi5 page (?src=, launchParameters) and the app (?src=, launch links) take the address through it.
    • Each says That is not an http or https address: … for anything else.
  • Embed page (packages/embed/site/embed.js): its own packageUrl.
    • A ?src= that is not http or https is refused with The package address is not an http or https URL., posted to the parent as error with code: 'refused', as other refusals are.
    • Before, a data: URL was taken for another site's package and played untrusted.
  • cmi5 (packages/cmi5/src/index.ts): startCmi5 gives the element only an http or https address, resolved against the page's address (url).
    • Anything else is a new unsafe-src event, like unsafe-return-url, and the next source is tried: the address's ?src=, then launchParameters.
    • webAddress(value, base?) takes the base.
    • session.src is now the absolute address the element was given.
  • Docs: the cmi5 README and docs/cmi5.md list unsafe-src and the rule; the embed README says what src= takes; AGENTS.md records the rule and why, so the next place that sets src follows it instead of dismissing another alert.

Why CodeQL accepts this

  • CodeQL's model of the URL constructor carries taint from its argument into search, hash and searchParams only, so the parsed URL's href is clean to all three queries (js/xss, js/xss-through-dom, js/client-side-unvalidated-url-redirection).
  • A check on the text instead, such as startsWith('https:'), satisfies js/xss only.
  • The redirect query accepts nothing short of a fixed host, which a player of any host's packages cannot have.
  • The app already set src from a parsed URL, and CodeQL never flagged it.

Testing

  • cmi5, 50 tests (the earlier ones, plus):
    • a relative package resolved against the page's address;
    • a javascript: package in the address and in launchParameters, each reported as unsafe-src and never given to the element;
    • webAddress with a base.
  • embed, 28 tests (27 before): a javascript: and a data: package refused with the message, and nothing set as src.
  • demo, 28 tests (the app's): a javascript: link shows the message on the home screen, sets no src, and raises no page error.
  • Typecheck passes for cmi5, embed and the demo.
  • By hand, the demo's player and cmi5 pages (no tests there, by the repository's rule) in Chrome against the dev server:
Page Address src State Message
Player javascript:alert(1) none idle That is not an http or https address: javascript:alert(1)
Player /demo/content/quiz.h5p http://localhost:…/demo/content/quiz.h5p ready
cmi5 (simulated) javascript:alert(1) none idle That is not an http or https address: javascript:alert(1)
cmi5 (simulated) /demo/content/quiz.h5p http://localhost:…/demo/content/quiz.h5p ready

No page errors and no dialogs in any of the four.

Release

Both are security fixes under docs/compatibility.md: they refuse something hostile.

  • embed: a patch.
  • cmi5: a minor, since the unsafe-src event is new in its types; a switch over Cmi5Event with no default branch must handle it.

Their notes should list the fix first.

Not changed: the element itself, which still takes any URL a host sets, and the three js/incomplete-html-attribute-sanitization alerts in content-check.ts, which are a different rule.

🤖 Generated with Claude Code

An address from a link, a text field or the cmi5 launch was set as the
element's src as written. The element only fetches it, but CodeQL cannot
know that: it flagged each place under js/xss, js/xss-through-dom and
js/client-side-unvalidated-url-redirection, and 22 alerts dismissed as
false positives came back whenever a line around them moved.

- The demo's player page, cmi5 page and app take the address through
  packageUrl (page-common.js), and the embed page through its own copy:
  resolved with new URL, http and https only, the parsed URL's href set.
  Anything else is refused with a message and plays nothing.
- startCmi5 gives the element only an http or https address, resolved
  against the page's (webAddress takes a base); anything else is an
  unsafe-src event, and the next source is tried.
- CodeQL's model of new URL(x) carries taint to search, hash and
  searchParams only, so the href is clean to all three queries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alekswebnet
alekswebnet merged commit 9696efe into main Oct 23, 2026
7 checks passed
@alekswebnet
alekswebnet deleted the fix/package-url-scheme branch October 27, 2026 20:04
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.

A package address reaches src as text, and CodeQL files the same alerts after every change

1 participant