atlas-iac/testing/tests/test_hermes_hux_ui_artifacts.mjs

362 lines
18 KiB
JavaScript
Raw Permalink Normal View History

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { createArtifactEndpointContract } from "../../dockerfiles/hermes-webui-hux/artifacts/endpoints.ts";
import {
ARTIFACTS_FLAG,
FOUNDATION_FLAG,
PROJECTS_FLAG,
artifactsEnabled,
normalizeArtifact,
normalizeArtifactWorkspace,
normalizePreview,
preservesImmutableHistory,
} from "../../dockerfiles/hermes-webui-hux/artifacts/model.ts";
import {
ArtifactContractError,
isMime,
isOpaqueId,
isSha256,
isUtc,
normalizeIdentity,
normalizeScope,
safeBlobUrl,
safePreviewText,
safeText,
sameIdentity,
sameScope,
scopedPath,
} from "../../dockerfiles/hermes-webui-hux/artifacts/security.ts";
const IDENTITY = {
tenantRef: "tnt_0123456789abcdef",
userRef: "usr_0123456789abcdef",
surface: "chat",
};
const RAW_IDENTITY = {
tenant_ref: IDENTITY.tenantRef,
user_ref: IDENTITY.userRef,
surface: IDENTITY.surface,
};
const SCOPE = {projectId: "prj_test1234", conversationId: "conv_test1234"};
const RAW_SCOPE = {project_id: SCOPE.projectId, conversation_id: SCOPE.conversationId};
const FLAGS = [FOUNDATION_FLAG, PROJECTS_FLAG, ARTIFACTS_FLAG];
const HASH_A = `sha256:${"a".repeat(64)}`;
const HASH_B = `sha256:${"b".repeat(64)}`;
const CONTRACT_TYPES = JSON.parse(
readFileSync("services/hermes/contracts/hux/artifact.schema.json"),
).properties.type.enum;
function version(number = 1, extra = {}) {
return {
version: number,
created_at: `2026-08-24T10:00:0${number}Z`,
created_by: {type: "assistant", id: "hermes", display: "Hermes"},
content_ref: {hash: number === 1 ? HASH_A : HASH_B, bytes: 120 * number, mime: "text/markdown"},
...(number > 1 ? {diff_from: number - 1} : {}),
note: `Draft ${number}`,
...extra,
};
}
function artifact(extra = {}) {
return {
schema: "hux.artifact.v1",
id: "art_test1234",
owner: IDENTITY.userRef,
conversation_id: SCOPE.conversationId,
project_id: SCOPE.projectId,
type: "markdown",
language: "report",
title: "Release report",
current_version: 2,
versions: [version(1), version(2)],
sensitivity: "personal",
created_at: "2026-08-24T10:00:01Z",
updated_at: "2026-08-24T10:00:02Z",
...extra,
};
}
function page(artifacts = [artifact()], extra = {}) {
return {
schema: "hux.artifact_workspace.v1",
api_version: "hux.v1",
identity: RAW_IDENTITY,
binding: RAW_SCOPE,
artifacts,
attachments: [{artifact_id: "art_test1234", source_ids: ["src_test1234"],
citation_ids: ["cit_test1234"]}],
authorizations: [{artifact_id: "art_test1234", download: "authorized",
share: "requires_approval"}],
...extra,
};
}
test("artifacts require the foundation and projects", () => {
assert.equal(artifactsEnabled(), false);
assert.equal(artifactsEnabled([]), false);
assert.equal(artifactsEnabled([ARTIFACTS_FLAG]), false);
assert.equal(artifactsEnabled([FOUNDATION_FLAG, ARTIFACTS_FLAG]), false);
assert.equal(artifactsEnabled([PROJECTS_FLAG, ARTIFACTS_FLAG]), false);
assert.equal(artifactsEnabled(FLAGS), true);
});
test("security primitives reject malformed scope and unsafe display data", () => {
assert.equal(isOpaqueId("art_test1234", "art"), true);
assert.equal(isOpaqueId("prj_test1234", "art"), false);
assert.equal(isOpaqueId("bad"), false);
assert.equal(isOpaqueId(4), false);
assert.equal(isUtc("2026-08-24T10:00:00.123Z"), true);
assert.equal(isUtc("2026-99-24T10:00:00Z"), false);
assert.equal(isUtc(4), false);
assert.equal(isSha256(HASH_A), true);
assert.equal(isSha256(`sha256:${"z".repeat(64)}`), false);
assert.equal(isSha256(4), false);
assert.equal(isMime("text/plain"), true);
assert.equal(isMime("bad"), false);
assert.equal(isMime("a/" + "b".repeat(121)), false);
assert.equal(isMime(4), false);
assert.equal(safeText({}, "fallback", 20), "fallback");
assert.equal(safeText(" \n ", "fallback", 20), "fallback");
assert.equal(safeText("abc\u0000def", "fallback", 20), "abc def");
assert.equal(safeText("abcdef", "fallback", 4), "abc…");
assert.equal(safeText("token=secret Bearer abc.DEF", "fallback", 200), "[redacted] [redacted]");
assert.match(safeText("-----BEGIN PRIVATE KEY----- secret -----END PRIVATE KEY-----", "x", 200), /redacted/);
assert.equal(safePreviewText("line one\nline two"), "line one\nline two");
assert.equal(safePreviewText("bad\u0000content"), null);
assert.equal(safePreviewText("abc", 2), null);
assert.equal(safePreviewText({}), null);
assert.equal(safeBlobUrl("blob:https://chat.example/abc"), "blob:https://chat.example/abc");
for (const unsafe of ["https://example.test/image.png", "data:image/png;base64,x", "blob:has space", 4]) {
assert.equal(safeBlobUrl(unsafe), null);
}
});
test("identity and binding normalization fail closed", () => {
assert.deepEqual(normalizeIdentity(RAW_IDENTITY), IDENTITY);
assert.deepEqual(normalizeScope(RAW_SCOPE), SCOPE);
assert.equal(sameIdentity(IDENTITY, {...IDENTITY}), true);
assert.equal(sameIdentity(IDENTITY, {...IDENTITY, surface: "worker"}), false);
assert.equal(sameScope(SCOPE, {...SCOPE}), true);
assert.equal(sameScope(SCOPE, {...SCOPE, projectId: "prj_other1234"}), false);
for (const bad of [null, [], {}, {...RAW_IDENTITY, tenant_ref: "tenant"},
{...RAW_IDENTITY, user_ref: "user"}, {...RAW_IDENTITY, surface: "browser"}]) {
assert.throws(() => normalizeIdentity(bad), ArtifactContractError);
}
for (const bad of [null, [], {}, {...RAW_SCOPE, project_id: "bad"},
{...RAW_SCOPE, conversation_id: "bad"}]) {
assert.throws(() => normalizeScope(bad), ArtifactContractError);
}
});
test("schema types select safe renderers without live HTML or SVG", () => {
const cases = [
["markdown", "text/markdown", "", "document"],
["markdown", "text/plain", "research", "report"],
["document", "application/pdf", "analysis", "report"],
["document", "text/plain", "", "document"],
["code", "text/x-python", "python", "code"],
["html", "text/html", "html", "code"],
["svg", "image/svg+xml", "svg", "code"],
["json", "application/json", "json", "data"],
["json", "text/plain", "json", "data"],
["csv", "text/csv", "csv", "data"],
["csv", "text/plain", "csv", "data"],
["image", "image/png", "", "image"],
["image", "image/jpeg", "", "image"],
["image", "image/webp", "", "image"],
["image", "image/gif", "", "image"],
["audio", "audio/wav", "", "audio"],
["audio", "audio/mpeg", "", "audio"],
["audio", "audio/ogg", "", "audio"],
["audio", "audio/webm", "", "audio"],
];
assert.deepEqual(new Set(cases.map(([type]) => type)), new Set(CONTRACT_TYPES));
for (const [type, mime, language, renderer] of cases) {
const raw = artifact({type, language, current_version: 1,
versions: [version(1, {content_ref: {hash: HASH_A, bytes: 1, mime}})]});
assert.equal(normalizeArtifact(raw, IDENTITY.userRef, SCOPE)?.renderer, renderer);
}
});
test("valid artifact preserves immutable content metadata and promotion", () => {
const raw = artifact({
promotion: {project_id: SCOPE.projectId, version: 1, at: "2026-08-24T10:00:03Z"},
versions: [version(1), version(2, {
lineage: {artifact_id: "art_source1234", version: 3},
created_by: {type: "user", id: "private-user"},
})],
});
const item = normalizeArtifact(raw, IDENTITY.userRef, SCOPE);
assert.equal(item.promotedVersion, 1);
assert.equal(item.renderer, "report");
assert.equal(item.versions[1].createdBy, "user");
assert.deepEqual(item.versions[1].lineage, {artifactId: "art_source1234", version: 3});
assert.doesNotMatch(JSON.stringify(item), /private-user|Hermes/);
});
test("artifact records reject malformed, rewritten, and cross-scope history", () => {
const bad = [
artifact({schema: "hux.artifact.v0"}), artifact({id: "bad"}),
artifact({owner: "usr_ffffffffffffffff"}), artifact({project_id: "prj_other1234"}),
artifact({conversation_id: "conv_other1234"}), artifact({type: "binary"}),
artifact({current_version: 0}), artifact({current_version: 3}), artifact({versions: []}),
artifact({versions: "no"}), artifact({versions: Array.from({length: 201}, () => version(1))}),
artifact({sensitivity: "secret"}), artifact({created_at: "today"}),
artifact({updated_at: "today"}), artifact({updated_at: "2026-08-24T09:00:00Z"}),
artifact({title: ""}), artifact({title: {private: true}}),
artifact({versions: [version(1), version(1)]}),
artifact({versions: [version(1), version(2, {created_at: "2026-08-24T09:00:00Z"})]}),
artifact({versions: [version(1), version(2, {diff_from: 2})]}),
artifact({versions: [version(1), version(2, {lineage: {artifact_id: "art_test1234", version: 2}})]}),
artifact({promotion: null}),
artifact({promotion: {project_id: "prj_other1234", version: 1, at: "2026-08-24T10:00:03Z"}}),
artifact({promotion: {project_id: SCOPE.projectId, version: 3, at: "2026-08-24T10:00:03Z"}}),
artifact({promotion: {project_id: SCOPE.projectId, version: 1, at: "today"}}),
artifact({type: "image", current_version: 1, versions: [version(1)]}),
];
for (const raw of bad) assert.equal(normalizeArtifact(raw, IDENTITY.userRef, SCOPE), null);
const badVersions = [
version(1, {version: 0}), version(1, {created_at: "today"}), version(1, {created_by: null}),
version(1, {created_by: {type: "intruder", id: "x"}}),
version(1, {created_by: {type: "user", id: ""}}),
version(1, {content_ref: null}), version(1, {content_ref: {hash: "bad", bytes: 1, mime: "text/markdown"}}),
version(1, {content_ref: {hash: HASH_A, bytes: -1, mime: "text/markdown"}}),
version(1, {content_ref: {hash: HASH_A, bytes: 1, mime: "bad"}}),
version(1, {diff_from: 0}), version(1, {lineage: null}),
version(1, {lineage: {artifact_id: "bad", version: 1}}),
version(1, {lineage: {artifact_id: "art_source1234", version: 0}}),
];
for (const invalid of badVersions) {
assert.equal(normalizeArtifact(artifact({current_version: 1, versions: [invalid]}),
IDENTITY.userRef, SCOPE), null);
}
});
test("workspace envelope enforces tenant, user, project, and conversation binding", () => {
const result = normalizeArtifactWorkspace(page(), IDENTITY, SCOPE);
assert.equal(result.rejected, 0);
assert.equal(result.artifacts.length, 1);
assert.deepEqual(result.attachments, [{artifactId: "art_test1234", sourceCount: 1, citationCount: 1}]);
assert.deepEqual(result.authorizations, [{artifactId: "art_test1234", download: "authorized",
share: "requires_approval"}]);
for (const bad of [null, {schema: "old"}, page([], {api_version: "hux.v0"}),
page([], {identity: {...RAW_IDENTITY, tenant_ref: "tnt_ffffffffffffffff"}}),
page([], {identity: {...RAW_IDENTITY, user_ref: "usr_ffffffffffffffff"}}),
page([], {identity: {...RAW_IDENTITY, surface: "worker"}}),
page([], {binding: {...RAW_SCOPE, project_id: "prj_other1234"}}),
page([], {binding: {...RAW_SCOPE, conversation_id: "conv_other1234"}})]) {
assert.throws(() => normalizeArtifactWorkspace(bad, IDENTITY, SCOPE), /scope boundary|identity/);
}
});
test("workspace rejects bad entries and ignores unsafe attachment metadata", () => {
const duplicate = artifact();
const result = normalizeArtifactWorkspace(page([artifact(), duplicate, artifact({id: "bad"})], {
attachments: [
{artifact_id: "art_test1234", source_ids: ["src_test1234"], citation_ids: []},
{artifact_id: "art_test1234", source_ids: [], citation_ids: []},
{artifact_id: "art_unknown1", source_ids: [], citation_ids: []},
{artifact_id: "art_other1234", source_ids: ["bad"], citation_ids: []},
null,
],
authorizations: [
{artifact_id: "art_test1234", download: "authorized", share: "unavailable"},
{artifact_id: "art_test1234", download: "authorized", share: "authorized"},
{artifact_id: "art_unknown1", download: "authorized", share: "authorized"},
{artifact_id: "art_other1234", download: "yes", share: "authorized"},
null,
],
}), IDENTITY, SCOPE);
assert.equal(result.artifacts.length, 1);
assert.equal(result.rejected, 2);
assert.equal(result.attachments.length, 1);
assert.equal(result.authorizations.length, 1);
assert.deepEqual(normalizeArtifactWorkspace(page([], {artifacts: null, attachments: {},
authorizations: {}}), IDENTITY, SCOPE).artifacts, []);
});
test("previews require matching identity, binding, hash, MIME, and local object URLs", () => {
const item = normalizeArtifact(artifact(), IDENTITY.userRef, SCOPE);
const base = {schema: "hux.artifact_preview.v1", identity: RAW_IDENTITY, binding: RAW_SCOPE,
artifact_id: item.id, version: 2, hash: HASH_B, mime: "text/markdown", text: "# Safe report"};
assert.equal(normalizePreview(base, item, IDENTITY, SCOPE)?.text, "# Safe report");
const invalid = [null, {...base, schema: "old"}, {...base, identity: {...RAW_IDENTITY, surface: "worker"}},
{...base, binding: {...RAW_SCOPE, project_id: "prj_other1234"}}, {...base, artifact_id: "art_other1234"},
{...base, version: 3}, {...base, hash: HASH_A}, {...base, mime: "text/plain"},
{...base, text: "bad\u0000text"}, {...base, blob_url: "blob:local"}];
for (const raw of invalid) assert.equal(normalizePreview(raw, item, IDENTITY, SCOPE), null);
assert.equal(normalizePreview(base, item, {...IDENTITY, userRef: "bad"}, SCOPE), null);
const image = normalizeArtifact(artifact({type: "image", language: "", current_version: 1,
versions: [version(1, {content_ref: {hash: HASH_A, bytes: 5, mime: "image/png"}})]}),
IDENTITY.userRef, SCOPE);
const imagePreview = {...base, artifact_id: image.id, version: 1, hash: HASH_A, mime: "image/png",
text: undefined, blob_url: "blob:https://chat.example/image"};
assert.equal(normalizePreview(imagePreview, image, IDENTITY, SCOPE)?.blobUrl,
"blob:https://chat.example/image");
assert.equal(normalizePreview({...imagePreview, blob_url: "https://remote/image"}, image,
IDENTITY, SCOPE), null);
assert.equal(normalizePreview({...imagePreview, text: "inline"}, image, IDENTITY, SCOPE), null);
});
test("immutable history permits append-only versions but not rewrites or orphaning", () => {
const before = normalizeArtifact(artifact(), IDENTITY.userRef, SCOPE);
const appendedRaw = artifact({current_version: 3, updated_at: "2026-08-24T10:00:03Z",
versions: [...artifact().versions, version(3, {content_ref: {hash: HASH_A, bytes: 360,
mime: "text/markdown"}})]});
const appended = normalizeArtifact(appendedRaw, IDENTITY.userRef, SCOPE);
assert.equal(preservesImmutableHistory(before, before), true);
assert.equal(preservesImmutableHistory(before, appended), true);
const changes = [
{...before, id: "art_other1234"}, {...before, owner: "usr_ffffffffffffffff"},
{...before, projectId: "prj_other1234"}, {...before, conversationId: "conv_other1234"},
{...before, type: "document"}, {...before, versions: before.versions.slice(1), currentVersion: 1},
{...before, versions: [{...before.versions[0], note: "rewritten"}, before.versions[1]]},
{...before, currentVersion: 1},
];
for (const changed of changes) assert.equal(preservesImmutableHistory(before, changed), false);
});
test("endpoint contract is default-off, scoped, immutable, and optimistic", () => {
const client = {apiVersion: "hux.v1", identity: IDENTITY,
endpoint(path) { assert.match(path, /^\/projects\/prj_[^/]+\/conversations\/conv_[^/]+\/artifacts/);
return `/hux/v1${path}`; }};
assert.equal(createArtifactEndpointContract(client, SCOPE), null);
assert.equal(createArtifactEndpointContract(client, SCOPE, [FOUNDATION_FLAG, ARTIFACTS_FLAG]), null);
const contract = createArtifactEndpointContract(client, SCOPE, FLAGS);
assert.equal(contract.list.path, "/hux/v1/projects/prj_test1234/conversations/conv_test1234/artifacts");
assert.equal(contract.item("art_test1234").method, "GET");
assert.match(contract.preview("art_test1234", 2).path, /versions\/2\/preview$/);
assert.deepEqual(contract.diff("art_test1234", 1, 2).requiredBody,
["from_version", "to_version", "expected_current_version"]);
assert.match(contract.appendVersion("art_test1234").path, /versions$/);
assert.match(contract.continueEdit("art_test1234").path, /continue$/);
assert.match(contract.authorize("art_test1234", "download").path, /authorizations\/download$/);
assert.match(contract.download("art_test1234", 1).path, /versions\/1\/download$/);
assert.match(contract.share("art_test1234", 1).path, /versions\/1\/shares$/);
assert.match(contract.promote("art_test1234").path, /promotions$/);
assert.match(contract.sources("art_test1234").path, /sources$/);
for (const call of [() => contract.item("bad"), () => contract.preview("art_test1234", 0),
() => contract.diff("art_test1234", 2, 1), () => contract.authorize("art_test1234", "execute")]) {
assert.throws(call, TypeError);
}
assert.throws(() => createArtifactEndpointContract({...client, apiVersion: "hux.v2"}, SCOPE, FLAGS),
/HUX v1/);
assert.throws(() => createArtifactEndpointContract({...client,
identity: {...IDENTITY, tenantRef: "bad"}}, SCOPE, FLAGS), /identity/);
});
test("scoped path rejects traversal and malformed identifiers", () => {
assert.equal(scopedPath("/projects", SCOPE, "art_test1234"),
"/projects/prj_test1234/conversations/conv_test1234/artifacts/art_test1234");
for (const call of [() => scopedPath("https://remote", SCOPE), () => scopedPath("//remote", SCOPE),
() => scopedPath("/../projects", SCOPE), () => scopedPath("/projects?x", SCOPE),
() => scopedPath("/projects", {...SCOPE, projectId: "bad"}),
() => scopedPath("/projects", {...SCOPE, conversationId: "bad"}),
() => scopedPath("/projects", SCOPE, "bad")]) assert.throws(call, TypeError);
});