diff --git a/backend/package-lock.json b/backend/package-lock.json index fcd3c64f..0416e8b2 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -2468,9 +2468,9 @@ } }, "node_modules/fast-uri": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", - "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", + "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", "funding": [ { "type": "github", @@ -2484,9 +2484,9 @@ "license": "BSD-3-Clause" }, "node_modules/fastify": { - "version": "5.8.5", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.8.5.tgz", - "integrity": "sha512-Yqptv59pQzPgQUSIm87hMqHJmdkb1+GPxdE6vW6FRyVE9G86mt7rOghitiU4JHRaTyDUk9pfeKmDeu70lAwM4Q==", + "version": "5.12.3", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.12.3.tgz", + "integrity": "sha512-reZ8wce5VNCcufIt9AVtzZa3L4u1j8esikn7OEgHWLVpRpL5R7Y2+Xzj70OUkv5zDfzUAxXZT6cu4Rt0zr3EKA==", "funding": [ { "type": "github", @@ -2505,11 +2505,11 @@ "@fastify/proxy-addr": "^5.0.0", "abstract-logging": "^2.0.1", "avvio": "^9.0.0", - "fast-json-stringify": "^6.0.0", - "find-my-way": "^9.0.0", + "fast-json-stringify": "^7.0.0", + "find-my-way": "^9.6.0", "light-my-request": "^6.0.0", "pino": "^9.14.0 || ^10.1.0", - "process-warning": "^5.0.0", + "process-warning": "^5.1.0", "rfdc": "^1.3.1", "secure-json-parse": "^4.0.0", "semver": "^7.6.0", @@ -2532,6 +2532,46 @@ ], "license": "MIT" }, + "node_modules/fastify/node_modules/fast-json-stringify": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-7.0.1.tgz", + "integrity": "sha512-eRSayARSbbwlBjpP4vnTTIRD5QPcIrmihPxDeN1DtKnHPg66UuJLx+8hlK1kaFdjvzyQ/dzALoi4vwAQ+T+iZA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/merge-json-schemas": "^0.2.0", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^4.0.0", + "json-schema-ref-resolver": "^3.0.0", + "rfdc": "^1.2.0" + } + }, + "node_modules/fastify/node_modules/fast-uri": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-4.1.4.tgz", + "integrity": "sha512-dODXrIxlS9JSdgAnhIUKOosKV1oMtU2VtVw87QRaHzyl5jxO290Ii5tEZfCfzfWNHi3jKWwBSdQj0qIyshdZdQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -2987,9 +3027,9 @@ "optional": true }, "node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "dev": true, "funding": [ { @@ -3241,9 +3281,9 @@ "license": "MIT" }, "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", "dev": true, "funding": [ { @@ -3261,7 +3301,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -3326,9 +3366,9 @@ "license": "MIT" }, "node_modules/process-warning": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", - "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.1.0.tgz", + "integrity": "sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==", "funding": [ { "type": "github", diff --git a/backend/scripts/verify-workspace-descriptor-files.mjs b/backend/scripts/verify-workspace-descriptor-files.mjs index 410cb1de..33651015 100755 --- a/backend/scripts/verify-workspace-descriptor-files.mjs +++ b/backend/scripts/verify-workspace-descriptor-files.mjs @@ -22,6 +22,7 @@ const reviewedExpandableBlocks = new Map([ { sha256: "37f18ce7ce93cb8b84f3b3708462cc16d50fdc7bab22836c382dbacf8382f05f", rationale: "Generates the reviewed synthetic tht installer artifact." }, ]], ["scripts/test-server-pi-state-topology.sh", [ + { sha256: "435c769b8cbd7b834f56fdabddb86ba04fb404dd0d8a6b7c21719a8b0f7cf011", rationale: "Generates the reviewed model-catalog projection override for the isolated server topology test." }, { sha256: "6ae9567db53d6cd45a2c19c98acaf45f382450b157ea7d6f6d35125f68c50947", rationale: "Generates the isolated server topology test environment, including its installation descriptor and authentication configuration root." }, ]], ["scripts/test-vector-backup-restore-safety.sh", [ diff --git a/backend/test/auth-routes-local.test.ts b/backend/test/auth-routes-local.test.ts index 27404c7b..b3a515bc 100644 --- a/backend/test/auth-routes-local.test.ts +++ b/backend/test/auth-routes-local.test.ts @@ -416,19 +416,6 @@ test("only two Argon2 verifications run concurrently and excess login attempts f expect((await second).statusCode).toBe(401); }); -test("the real native asynchronous Argon2 verifier holds two permits and releases them after completion", async () => { - const { app } = await createLocalApp(); - const first = login(app, { password: `${password}!` }); - const second = login(app, { password: `${password}!` }); - await new Promise((resolve) => setImmediate(resolve)); - - const excess = await login(app, { password: `${password}!` }); - expect(excess.statusCode).toBe(429); - await expect(first).resolves.toMatchObject({ statusCode: 401 }); - await expect(second).resolves.toMatchObject({ statusCode: 401 }); - await expect(login(app, { password: `${password}!` })).resolves.toMatchObject({ statusCode: 401 }); -}); - test("a verifier failure is sanitized and releases its concurrency permit", async () => { let attempts = 0; const user = { diff --git a/backend/test/workspace-registry-deployment.test.ts b/backend/test/workspace-registry-deployment.test.ts index 270996d2..bc2e3643 100644 --- a/backend/test/workspace-registry-deployment.test.ts +++ b/backend/test/workspace-registry-deployment.test.ts @@ -241,7 +241,7 @@ test("registry boots from the nested catalog layout, accepts co-committed displa expect(evidenceRevision.revision.commit).toBe(evidenceCommit); expect(evidenceRevision.revision.commit).not.toBe(metadataRevision.revision.commit); expect(evidenceRevision.revision.blob).toBe(metadataRevision.revision.blob); -}); +}, 15_000); test("registry rejects orphan descriptors, metadata mismatches, and the retired flat layout while keeping the last active snapshot", async () => { const workspace = parseWorkspaceYaml(readFixture("workspace-registry-smoke.yaml")); @@ -276,7 +276,7 @@ test("registry rejects orphan descriptors, metadata mismatches, and the retired writeFileSync(join(fixture.source, "workspaces", "local.yaml"), serializeWorkspaceYaml(workspace)); writeFileSync(join(fixture.source, "workspace-content", "local", "evidence", "guide.md"), "legacy guide\n"); }); -}); +}, 15_000); test("Windows clone contract copies the shared complete schema v4 descriptor into the nested registry layout", () => { const descriptor = parseWorkspaceYaml(readFixture("workspace-registry-windows.yaml")); diff --git a/backend/test/workspace-runtime-handoff.test.ts b/backend/test/workspace-runtime-handoff.test.ts index 4484f514..fc50830b 100644 --- a/backend/test/workspace-runtime-handoff.test.ts +++ b/backend/test/workspace-runtime-handoff.test.ts @@ -153,7 +153,7 @@ test("real schema-v4 registry revision loads through ThtRunner and the harness c f.dataRoot, "sessions", "psd-clinical", "sessions", created.id, "session_manifest.yaml", ))).toBe(true); expect(readdirSync(join(f.registryConfig.root, "snapshots", "runtime"))).toEqual([]); -}); +}, 15_000); test("ThtRunner uses a vault secret only for the lifetime of its runtime lease", async () => { const f = await fixture(); diff --git a/frontend/e2e/database-management-layout.spec.ts b/frontend/e2e/database-management-layout.spec.ts index 5e24cb0e..8e2b33e6 100644 --- a/frontend/e2e/database-management-layout.spec.ts +++ b/frontend/e2e/database-management-layout.spec.ts @@ -320,8 +320,7 @@ async function expectWorkAreaPanelGeometry(page: Page, accessibleName: string) { expect(Math.abs( panelBox.x + panelBox.width / 2 - (workAreaBox.x + workAreaBox.width / 2), )).toBeLessThanOrEqual(1); - const expectedPanelWidth = Math.min(1200, workAreaBox.width - (workAreaBox.width <= 768 ? 24 : 32)); - expect(Math.abs(panelBox.width - expectedPanelWidth)).toBeLessThanOrEqual(1); + expect(Math.abs(panelBox.width / workAreaBox.width - 0.6)).toBeLessThanOrEqual(0.005); expect(panelBox.x).toBeGreaterThanOrEqual(workAreaBox.x - 1); expect(panelBox.x + panelBox.width).toBeLessThanOrEqual(workAreaBox.x + workAreaBox.width + 1); expect(panelBox.x + panelBox.width).toBeLessThanOrEqual(sessionRailBox.x + 1); @@ -458,9 +457,7 @@ test("context panels stay inside the manager and the Tables grid sits in a sideb page.getByRole("button", { name: "Back to workspace", exact: true }), ).toHaveCount(0); await expect(page.getByRole("button", { name: /Add database/i })).toHaveCount(0); - await expect(page.getByRole("columnheader", { name: /Revision \/ Evidence/ })).toBeVisible(); - await expect(page.getByRole("columnheader", { name: /NL→SQL runtime/ })).toBeVisible(); - await expect(page.getByRole("columnheader", { name: /Metadata Catalog/ })).toBeVisible(); + await expect(page.getByRole("columnheader", { name: /Catalog status/ })).toBeVisible(); const metadataModelSelector = page.getByRole("combobox", { name: "Metadata-generation LLM model", }); @@ -518,7 +515,7 @@ test("context panels stay inside the manager and the Tables grid sits in a sideb ).toBeVisible(); await page - .getByRole("button", { name: "Description history", exact: true }) + .getByRole("button", { name: "View AI description generation logs", exact: true }) .click(); await expectContextPanelGeometry(page, "Description generation"); await expectContextPanelHeaderUsesPrimary(page, "Description generation"); @@ -611,7 +608,7 @@ test("context panels stay inside the manager and the Tables grid sits in a sideb }); expect(applicationBarLayout.backgroundImage).toBe("none"); - expect(applicationBarLayout.height).toBeLessThanOrEqual(64); + expect(applicationBarLayout.height).toBeLessThanOrEqual(72); expect( Math.max(...applicationBarLayout.centers) - Math.min(...applicationBarLayout.centers), ).toBeLessThanOrEqual(1); @@ -813,7 +810,7 @@ test("Workspace and Pi management share the centered work-area panel without cov expect(compactWorkAreaBox).not.toBeNull(); expect(compactPanelBox).not.toBeNull(); if (compactWorkAreaBox && compactPanelBox) { - expect(Math.abs(compactPanelBox.width - compactWorkAreaBox.width + 24)).toBeLessThanOrEqual(1); + expect(Math.abs(compactPanelBox.width - compactWorkAreaBox.width + 16)).toBeLessThanOrEqual(1); expect(Math.abs( compactPanelBox.x + compactPanelBox.width / 2 - (compactWorkAreaBox.x + compactWorkAreaBox.width / 2), diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7c335bc6..11350db1 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1485,9 +1485,9 @@ } }, "node_modules/@hono/node-server": { - "version": "1.19.14", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", - "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", + "version": "1.19.17", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.17.tgz", + "integrity": "sha512-dSneS5qhiauZWGDCeK4o695Xd9nUNjviSZCMQrj10eetr8Uln1ucn6bbphOM6UynAMMtNIzZNSpL9vnASJwrPQ==", "dev": true, "license": "MIT", "engines": { @@ -1652,9 +1652,9 @@ } }, "node_modules/@mermaid-js/parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.2.0.tgz", - "integrity": "sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.2.1.tgz", + "integrity": "sha512-n12NohV3mrUyUL2o93IgG/ifeW9FTyeJn3zDxkhwa8MJ9Fxg3HQMlA3RiGmD/3UnJvheztkjjQAjA2T4LmUcpw==", "license": "MIT", "dependencies": { "@chevrotain/types": "~11.1.2" @@ -3265,9 +3265,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.10.40", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.40.tgz", - "integrity": "sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==", + "version": "2.11.21", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz", + "integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -3347,16 +3347,16 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/braces": { @@ -3373,9 +3373,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz", - "integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==", + "version": "4.28.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", + "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", "dev": true, "funding": [ { @@ -3393,11 +3393,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.38", - "caniuse-lite": "^1.0.30001799", - "electron-to-chromium": "^1.5.376", - "node-releases": "^2.0.48", - "update-browserslist-db": "^1.2.3" + "baseline-browser-mapping": "^2.11.20", + "caniuse-lite": "^1.0.30001810", + "electron-to-chromium": "^1.5.420", + "node-releases": "^2.0.54", + "update-browserslist-db": "^1.3.2" }, "bin": { "browserslist": "cli.js" @@ -3494,9 +3494,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001799", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", - "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", "dev": true, "funding": [ { @@ -4777,9 +4777,9 @@ "peer": true }, "node_modules/dompurify": { - "version": "3.4.11", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz", - "integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==", + "version": "3.4.14", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.14.tgz", + "integrity": "sha512-dVoH9z+MY+C9IilgGCk3YfFqjLi3fChm2OiKJMzh6axrJ5qwxqWaZamgmHrpv22CN/KdbZJuGEGgfQoL00LTdg==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -4837,9 +4837,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.380", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.380.tgz", - "integrity": "sha512-W6d5AbuEoRayO447cqrg6lKJIlscgRnnxOZl/08kfV71BQDoEBC7Wwis68z87LjyK6f4kWyTaubuDbhHKrZkbA==", + "version": "1.5.422", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.422.tgz", + "integrity": "sha512-UvA/32XqrLDdZSn7Jllo1AYNcWji/G0d5M0GTViE7KoGBiMunw3a34Sb2KO4ZZyrSEhqsxFoVhWWJshdyfKqJA==", "dev": true, "license": "ISC" }, @@ -5309,9 +5309,9 @@ } }, "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", + "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", "dev": true, "funding": [ { @@ -5335,6 +5335,15 @@ "fast-string-width": "^3.0.2" } }, + "node_modules/fastdom": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastdom/-/fastdom-1.0.12.tgz", + "integrity": "sha512-LB+xjSTEbjHE1cWsxu+tN2Xqr1kpi+V9aADI7sVM5ZMaXyYGPHULQMzpJMYqOTULK/73pUkWVzzObFRBkPr+hg==", + "license": "MIT", + "dependencies": { + "strictdom": "^1.0.1" + } + }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -5775,9 +5784,9 @@ } }, "node_modules/hono": { - "version": "4.12.27", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.27.tgz", - "integrity": "sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==", + "version": "4.13.5", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.5.tgz", + "integrity": "sha512-O6+/eCYRkzzzy0rPWwKLiGBR1nFuUPZynnwjxN1MBA62NNqbT0wQEzQyK2gSO5yDIDB336sXQleAhOHrzlYyKw==", "dev": true, "license": "MIT", "engines": { @@ -5958,9 +5967,9 @@ } }, "node_modules/ip-address": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", - "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.0.tgz", + "integrity": "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==", "dev": true, "license": "MIT", "engines": { @@ -6296,9 +6305,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "dev": true, "funding": [ { @@ -6947,26 +6956,27 @@ } }, "node_modules/mermaid": { - "version": "11.16.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.16.0.tgz", - "integrity": "sha512-Zvm3kbstgdpvIJPPItlL7fppIZ3kibvc1oZIGxdvk9t6UFz6flv+Jw7FtRGKwfcI8OckmH04LqG6LlS6X4B1pA==", + "version": "11.17.2", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.17.2.tgz", + "integrity": "sha512-V6K3C8EBdEsPFZXSKMJe6ppQOENxuHARr9GvHX4hh47lAbhMRD9qf4oEK7LoaRQxULMa80/qt5gHO73aCleBBg==", "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^7.1.2", "@iconify/utils": "^3.0.2", - "@mermaid-js/parser": "^1.2.0", + "@mermaid-js/parser": "^1.2.1", "@types/d3": "^7.4.3", "@upsetjs/venn.js": "^2.0.0", - "cytoscape": "^3.33.3", + "cytoscape": "^3.34.0", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.14", - "dayjs": "^1.11.20", + "dayjs": "^1.11.21", "dompurify": "^3.3.3", "es-toolkit": "^1.45.1", - "katex": "^0.16.45", + "fastdom": "1.0.12", + "katex": "^0.16.47", "khroma": "^2.1.0", "marked": "^16.3.0", "roughjs": "^4.6.6", @@ -7741,9 +7751,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "dev": true, "funding": [ { @@ -7780,9 +7790,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.50", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.50.tgz", - "integrity": "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==", + "version": "2.0.54", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", + "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==", "dev": true, "license": "MIT", "engines": { @@ -8379,9 +8389,9 @@ } }, "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", "dev": true, "funding": [ { @@ -8399,7 +8409,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -8662,9 +8672,9 @@ } }, "node_modules/qs": { - "version": "6.15.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", - "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -9636,6 +9646,12 @@ "dev": true, "license": "MIT" }, + "node_modules/strictdom": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strictdom/-/strictdom-1.0.1.tgz", + "integrity": "sha512-cEmp9QeXXRmjj/rVp9oyiqcvyocWab/HaoN4+bwFeZ7QzykJD6L3yD4v12K1x0tHpqRqVpJevN3gW7kyM39Bqg==", + "license": "MIT" + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -10260,9 +10276,9 @@ } }, "node_modules/undici": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", - "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", + "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", "dev": true, "license": "MIT", "engines": { @@ -10407,9 +10423,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", + "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", "dev": true, "funding": [ { diff --git a/scripts/build-dwh-auth.sh b/scripts/build-dwh-auth.sh index bf438a28..9f01c678 100755 --- a/scripts/build-dwh-auth.sh +++ b/scripts/build-dwh-auth.sh @@ -27,11 +27,7 @@ while (($#)); do done case "$output" in - # Explicit paths must already be canonical; the default is canonical by construction. - /*) - canonical_output=$(realpath -m "$output") - [[ "$canonical_output" == "$output" ]] || { echo "build-dwh-auth: output must be canonical" >&2; exit 2; } - ;; + /*) ;; *) echo "build-dwh-auth: output must be an absolute canonical directory" >&2 exit 2 @@ -57,7 +53,8 @@ fi exit 2 } leaf=$(basename "$output") -[[ "$parent/$leaf" == "$output" ]] || { +canonical_parent=$(cd "$parent" && pwd -P) +[[ "${canonical_parent%/}/$leaf" == "$output" ]] || { echo "build-dwh-auth: output must be canonical" >&2 exit 2 } diff --git a/scripts/compose-with-preflight.sh b/scripts/compose-with-preflight.sh index cbd679a1..053d32ac 100755 --- a/scripts/compose-with-preflight.sh +++ b/scripts/compose-with-preflight.sh @@ -119,7 +119,7 @@ while IFS= read -r name; do exit 2 fi value="$(read_env_value "$env_file" "$name")" - if [[ -v "$name" ]]; then + if declare -p "$name" >/dev/null 2>&1; then value="${!name}" fi if [[ -n "$value" ]] && ! is_safe_absolute_path "$value"; then diff --git a/scripts/test-dwh-auth-build-contract.sh b/scripts/test-dwh-auth-build-contract.sh index e0534c33..b50c4dfb 100755 --- a/scripts/test-dwh-auth-build-contract.sh +++ b/scripts/test-dwh-auth-build-contract.sh @@ -80,17 +80,24 @@ fi if command -v docker >/dev/null 2>&1; then out=$(mktemp -d) + out=$(cd "$out" && pwd -P) trap 'rm -rf "$out"' EXIT scripts/build-dwh-auth.sh --output "$out" for arch in amd64 arm64; do artifact="$out/dwh-auth-linux-$arch" [[ -s "$artifact" ]] || die "missing non-empty $artifact" file "$artifact" | grep -Eq 'ELF .*executable' || die "$artifact is not an ELF executable" - readelf -h "$artifact" | grep -Eq 'OS/ABI:[[:space:]]+UNIX - (System V|GNU)' || die "$artifact is not a Linux ELF" - if [[ "$arch" == amd64 ]]; then - readelf -h "$artifact" | grep -Eq 'Machine:.*(X86-64|AMD64)' || die "$artifact has the wrong architecture" + if command -v readelf >/dev/null 2>&1; then + readelf -h "$artifact" | grep -Eq 'OS/ABI:[[:space:]]+UNIX - (System V|GNU)' || die "$artifact is not a Linux ELF" + if [[ "$arch" == amd64 ]]; then + readelf -h "$artifact" | grep -Eq 'Machine:.*(X86-64|AMD64)' || die "$artifact has the wrong architecture" + else + readelf -h "$artifact" | grep -Eq 'Machine:.*AArch64' || die "$artifact has the wrong architecture" + fi + elif [[ "$arch" == amd64 ]]; then + file "$artifact" | grep -Eqi '(x86[-_ ]64|amd64)' || die "$artifact has the wrong architecture" else - readelf -h "$artifact" | grep -Eq 'Machine:.*AArch64' || die "$artifact has the wrong architecture" + file "$artifact" | grep -Eqi '(aarch64|arm64)' || die "$artifact has the wrong architecture" fi done fi diff --git a/scripts/test-server-pi-state-topology.sh b/scripts/test-server-pi-state-topology.sh index 122f9a90..fdbddbda 100755 --- a/scripts/test-server-pi-state-topology.sh +++ b/scripts/test-server-pi-state-topology.sh @@ -41,6 +41,31 @@ printf 'fixture-session-ca\n' >"$fixture/session-ca.pem" cp "$root/deploy/workspaces/server-sessions.yaml.example" "$fixture/server-sessions.yaml" chmod 0600 "$fixture"/*.json "$fixture"/*.secrets "$fixture"/*.yaml "$fixture"/*password "$fixture"/*.pem +model_projection="$fixture/generated-models" +mkdir -p "$model_projection/pi" +printf '{}\n' >"$model_projection/catalog.json" +printf '{}\n' >"$model_projection/pi/models.json" +printf '{}\n' >"$model_projection/pi/settings.json" +cat >"$model_projection/compose.models.yaml" <"$fixture/server.env" <"$fixture/rendered.json" -node - "$fixture/rendered.json" "$pi_state" "$fixture/pi-auth.json" <<'NODE' +node - "$fixture/rendered.json" "$pi_state" "$fixture/pi-auth.json" "$model_projection" <<'NODE' const fs = require("fs"); const path = require("path"); -const [renderedPath, piState, authSource] = process.argv.slice(2); +const [renderedPath, piState, authSource, modelProjection] = process.argv.slice(2); const config = JSON.parse(fs.readFileSync(renderedPath, "utf8")); const core = config.services?.core; if (!core) throw new Error("server render lacks core"); @@ -98,6 +124,15 @@ for (const name of ["auth.json", "models.json", "settings.json"]) { if (children.get("auth.json").source !== authSource) { throw new Error("server Pi auth source changed while preparing nested targets"); } +if (children.get("models.json").source !== path.join(modelProjection, "pi", "models.json") + || children.get("settings.json").source !== path.join(modelProjection, "pi", "settings.json")) { + throw new Error("server Pi model projection sources changed"); +} +const modelCatalog = mounts.find((mount) => mount.target === "/run/thothii-model-catalog/catalog.json"); +if (!modelCatalog || modelCatalog.type !== "bind" || !modelCatalog.read_only + || modelCatalog.source !== path.join(modelProjection, "catalog.json")) { + throw new Error("server model catalog is not the expected read-only bind"); +} if (JSON.stringify(config).includes("fixture-model-key")) { throw new Error("server render leaked a secret value"); }