Harden HTML export extension#1
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 833b623a76
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| child.on('error', () => {}); | ||
| child.unref(); | ||
| return true; |
There was a problem hiding this comment.
Wait for open command failure before reporting success
openArtifact now returns true immediately after spawn, but spawn reports missing/failed commands asynchronously via the error event. On Linux hosts without xdg-open (common in headless/server environments), this path still reports “Opened … in your default browser” even though nothing was launched, which is a user-visible false success regression from the previous awaited execFile behavior.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Addressed in f5fcac7. openArtifact now resolves the launcher before spawning and waits for immediate spawn errors or quick non-zero exits before reporting the browser-open success notification. Added regression coverage for missing PATH launchers via resolveOpenCommand. Verified with pnpm test, node --check index.js && node --check test/extension.test.js, and Pi/OMP offline /html-last smoke checks.
833b623 to
f5fcac7
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f5fcac7d51
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const forcedMode = resolveForcedExportMode(args); | ||
| let mode = forcedMode || 'local'; | ||
| if (mode === 'choose') { | ||
| mode = ctx && ctx.hasUI ? await chooseCommandExportMode(ctx) : 'local'; |
There was a problem hiding this comment.
Honor chooser mode when select UI is available
/html-last choose only enters the chooser path when ctx.hasUI is truthy, even though chooser support is actually determined by ctx.ui.select. In command contexts that provide ui.select but omit hasUI (which is plausible given other handlers only check ctx.ui), this branch falls back to local and immediately exports instead of presenting choices, so the new choose argument does not behave as documented. The gate here should align with chooseCommandExportMode and check for selectable UI capability rather than hasUI.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Addressed in c754578. /html-last choose now gates on selectable UI capability (ctx.ui.select) rather than ctx.hasUI, so command contexts that provide ui.select without hasUI still open the chooser. Added a focused hasSelectableUi regression assertion and re-ran tests/smokes.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ff59d70ac9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const BLOCKED_META_REFRESH = /<\s*meta\b[^>]*http-equiv\s*=\s*(['"]?)refresh\1/i; | ||
| const EVENT_HANDLER_ATTR = /\s+on[a-z]+\s*=/i; | ||
| const JAVASCRIPT_URL_ATTR = /\s(?:href|src|xlink:href|action|formaction)\s*=\s*(['\"]?)\s*javascript:/i; | ||
| const EXTERNAL_ASSET_ATTR = /\s(?:src|poster)\s*=\s*(['\"]?)\s*(?:https?:)?\/\//i; |
There was a problem hiding this comment.
Reject external assets loaded via srcset attributes
The external-asset validator only checks src and poster, so rich output like <img src="data:..." srcset="https://example.com/a.png 1x"> passes validation and is written to disk. When the artifact is opened, the browser will still fetch that remote URL, which bypasses the new “no external assets” safety boundary and leaks network metadata from local sessions.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Addressed in c754578. The rich HTML external asset check now rejects remote URLs in srcset attributes, including cases where src itself is local/data. Added a srcset="https://..." regression case under rich validation.
| const EVENT_HANDLER_ATTR = /\s+on[a-z]+\s*=/i; | ||
| const JAVASCRIPT_URL_ATTR = /\s(?:href|src|xlink:href|action|formaction)\s*=\s*(['\"]?)\s*javascript:/i; | ||
| const EXTERNAL_ASSET_ATTR = /\s(?:src|poster)\s*=\s*(['\"]?)\s*(?:https?:)?\/\//i; | ||
| const EXTERNAL_CSS_URL = /url\(\s*(['\"]?)\s*(?:https?:)?\/\//i; |
There was a problem hiding this comment.
Block remote CSS loaded through @import directives
The CSS external-resource check only looks for url(http...), so a style block using @import "https://example.com/x.css"; is currently accepted by validateRichHtmlDocument. That means rich exports can still pull third-party stylesheets at open time, contradicting the inline-only hardening and weakening the security model introduced in this commit.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Addressed in c754578. The CSS external-resource check now rejects remote @import directives in addition to url(http...). Added an @import "https://..." regression case under rich validation.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c754578a08
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const BLOCKED_META_REFRESH = /<\s*meta\b[^>]*http-equiv\s*=\s*(['"]?)refresh\1/i; | ||
| const EVENT_HANDLER_ATTR = /\s+on[a-z]+\s*=/i; | ||
| const JAVASCRIPT_URL_ATTR = /\s(?:href|src|xlink:href|action|formaction)\s*=\s*(['\"]?)\s*javascript:/i; | ||
| const EXTERNAL_ASSET_ATTR = /\s(?:(?:src|poster)\s*=\s*(['\"]?)\s*(?:https?:)?\/\/|srcset\s*=\s*(['\"]?)[^'\">]*(?:https?:)?\/\/)/i; |
There was a problem hiding this comment.
Reject external asset URLs in href-like SVG attrs
validateRichHtmlDocument promises to block external assets, but the EXTERNAL_ASSET_ATTR pattern only checks src, poster, and srcset. Rich HTML containing SVG asset elements like <image href="https://example.com/a.png"> or <use xlink:href="https://example.com/s.svg#id"> passes validation and is written to disk, and browsers will fetch those URLs when the artifact is opened. This bypasses the new no-external-assets safety boundary for rich exports.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Agreed, this is worth addressing because SVG image/use hrefs can fetch remote assets at open time. Addressed in 8bb6989: the rich HTML asset validator now rejects remote href/xlink:href on SVG asset tags (image, use, feImage) and includes regression cases for both <image href="https://..."> and <use xlink:href="https://...">. Verified with pnpm test, syntax checks, Pi/OMP /html-last smokes, and git diff --check.
Summary
/html-lastdefault to local export and add Pi/OMP-compatible input interception to prevent slash command fallthrough into the agent spinner pathVerification
pnpm testnode --check index.js && node --check test/extension.test.jspi -e ./index.js --offline --no-tools -p "/html-last"(completed; Pi emitted only the known model-pattern warning)omp -e ./index.js --offline --no-tools -p "/html-last"Notes