mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-02 15:04:37 +01:00
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:
1 parent
d258bcffb0
commit
9dea818e34
28 files changed
+554
-144
No files matched your search
@@ -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;
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
declare module 'html-to-vdom' {
|
||||
export default function HTMLToVDOM(dependencies: { VNode: Function; VText: Function }): (html: string) => any;
|
||||
}
|
||||
@@ -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 ──
|
||||
|
||||
Reference in new issue
Block a user