v1.91.122.0 fix(deps): clear the 2026-11-30 OSV suppressions, replace #3228, exempt dependabot from Version Gate rule 1 (#3239)

* fix(deps): clear the OSV suppressions that expire on 2026-11-30

Delete the hono, @anthropic-ai/sdk (#2754) and sprintf-js ignores, which the
lockfiles no longer match. Override image-size to 2.0.4 (#2753) and chokidar
to 4.0.3 in lib/diagram-render (drops braces), and delete those ignores.
Re-date katex to 2027-01-31: its fix is outside mermaid 11's range
(mermaid-js/mermaid#8388).

html-to-docx 1.8.0 bundles its own image-size 1.x, which the override cannot
reach and whose ICNS parser loops forever. make-pdf --to docx now hands it
only PNG, JPEG, GIF, WebP or SVG bytes. A golden docx test proves the Word
output is unchanged.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* build(deps-dev): @types/bun 1.4.3, prettier 3.9.10; hold the Anthropic SDKs to patches

Replaces dependabot #3228 without its agent-sdk 0.3 and sdk 0.133 bumps,
which break typecheck:test and overlay-sdk-cancel-eof. Their 0.x breaking
releases are semver-minor to dependabot, so the ignore covers minor and major.
@types/bun 1.4.3 retires 18 test type diagnostics; the baseline locks that in.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(ci): exempt dependabot dependency-manifest PRs from Version Gate rule 1

Dependabot never edits VERSION, so hard rule 1 failed every package.json PR
it opened. The workflow now passes the PR author and changed-file list; the
gate skips rule 1 only for dependabot[bot] when every file is a package.json,
bun.lock(b) or .github/dependabot.yml. A failed file lookup keeps the rule.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(make-pdf): guard every html-to-docx sizing path, keep non-ICNS formats

Review found three spellings that got past the regex guard and still hung
--to docx: a data-src decoy, a percent-encoded separator, and an unquoted
encoded colon. html-to-docx sizes images on three paths, and buildImage
URI-decodes the whole src first. The guard now mirrors all three, drops ICNS
(the only input its bundled image-size routes to the looping parser) instead
of allowlisting formats, so BMP and TIFF embed as before, and re-parses the
result with html-to-docx's own html-to-vdom to fail closed on anything the
regex misreads. The golden test tolerates CRLF checkouts on Windows.

Version Gate treats a failed file lookup as an empty list, so a partial
paginated result cannot decide the exemption.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* chore(release): stamp v1.91.121.0

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* chore(release): restamp v1.91.122.0

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
Garry Tanandcapy-ai[bot] authored and GitHub committed 2026-10-17 16:47:50 +00:00
1 parent d258bcffb0
commit 9dea818e34
28 files changed
+554 -144

No files matched your search

+78
View File
@@ -0,0 +1,78 @@
/**
* DOCX image guard. html-to-docx 1.8.0 sizes every data-URI <img> with its
* own bundled copy of image-size 1.x, which the package.json override cannot
* reach. That copy's detector sends a buffer to its ICNS parser exactly when
* the bytes start with "icns", and the parser loops forever on a zero-length
* entry (GHSA-w3rx-r6r6-pgpr). No other parser in the copy has a known flaw.
*
* guardDocxImages drops the <img> tags it can see that would size unsafe
* bytes, then re-parses the result with the same html-to-vdom html-to-docx
* uses and refuses the document if any <img> it would size is still unsafe,
* so a tag the regex misreads fails closed instead of hanging.
*/
import HTMLToVDOM from "html-to-vdom";
/** html-to-docx's data-URI pattern, shared by all three of its sizeOf call sites. */
const DATA_URI = /^data:([A-Za-z-+\/]+);base64,(.+)$/;
const IMG_TAG = /<img\b[^>]*>/gi;
const SRC_ATTR = /(?:^|\s)src\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i;
interface ImgNode { tagName?: string; properties?: { src?: unknown }; children?: ImgNode[] }
const toVdom = HTMLToVDOM({
VNode: function (this: ImgNode, tagName: string, properties: ImgNode["properties"], children: ImgNode[]) {
Object.assign(this, { tagName, properties, children });
},
VText: function () {},
});
function tryDecode(text: string): string | null {
try {
return decodeURIComponent(text);
} catch {
return null;
}
}
function isIcns(base64: string | null | undefined): boolean {
return base64 != null && Buffer.from(base64, "base64").toString("ascii", 0, 4) === "icns";
}
/**
* False when html-to-docx would hand ICNS bytes to its bundled sizer. Its two
* paragraph paths match the raw src and URI-decode the payload; buildImage
* URI-decodes the whole src, matches that, and sizes the payload as is.
*/
function sizesSafely(src: unknown): boolean {
if (typeof src !== "string") return true;
const raw = src.match(DATA_URI);
if (raw && isIcns(tryDecode(raw[2]))) return false;
return !isIcns(tryDecode(src)?.match(DATA_URI)?.[2]);
}
/** html-to-docx's minifyHTMLString, so the re-parse sees the string it parses. */
function minify(html: string): string {
return html.replace(/\n/g, " ").replace(/\r/g, " ").replace(/[\t]+</g, "<").replace(/>[\t ]+</g, "><").replace(/>[\t ]+$/g, ">");
}
function* imgSources(nodes: ImgNode | ImgNode[]): Generator<unknown> {
for (const node of Array.isArray(nodes) ? nodes : [nodes]) {
if (node.tagName === "img") yield node.properties?.src;
if (node.children) yield* imgSources(node.children);
}
}
export function guardDocxImages(html: string, warn: (msg: string) => void): string {
const guarded = html.replace(IMG_TAG, (tag) => {
const src = tag.match(SRC_ATTR);
if (sizesSafely(src?.[1] ?? src?.[2] ?? src?.[3])) return tag;
warn("docx: dropped an ICNS image (html-to-docx's bundled image-size hangs on it)");
return "";
});
for (const src of imgSources(toVdom(minify(guarded)))) {
if (!sizesSafely(src)) {
throw new Error("docx: an <img> tag hides an ICNS image, which html-to-docx's bundled image-size hangs on; convert or remove it");
}
}
return guarded;
}
+3
View File
@@ -0,0 +1,3 @@
declare module 'html-to-vdom' {
export default function HTMLToVDOM(dependencies: { VNode: Function; VText: Function }): (html: string) => any;
}
+2
View File
@@ -41,6 +41,7 @@ import {
substituteSlots,
} from "./diagram-prepass";
import { applyImagePolicy } from "./image-policy";
import { guardDocxImages } from "./docx-images";
/** Default output location (`$P generate letter.md` → /tmp/letter.pdf). */
export const OUTPUT_TMP_DIR = process.platform === "win32" ? os.tmpdir() : "/tmp";
@@ -174,6 +175,7 @@ export async function generate(opts: GenerateOptions): Promise<string> {
progress.end("Rasterizing diagrams for DOCX");
}
finalHtml = convertDiagnosticsForDocx(finalHtml);
finalHtml = guardDocxImages(finalHtml, warn);
}
// ─── --to html: write the self-contained document, no print round-trip ──