Spaces:
Running
Running
deploy(hf): sync szl-holdings/a11oy@2c09e5d7a552802149161edccf55d279e5aaec42 derived COPY set
Browse filesReusable Dockerfile-COPY-derived deploy from szl-holdings/a11oy 2c09e5d7a552802149161edccf55d279e5aaec42.
Files: 1169 Pruned: 0
Derived from Dockerfile COPY sources (NO hand-maintained allowlist).
Signed-off-by: SZL Holdings <noreply@szlholdings.ai>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
packages/receipt-substrate/src/index.ts
CHANGED
|
@@ -503,6 +503,39 @@ const DIGESTED_SOURCE_QUERY_KEYS = new Set(["code"]);
|
|
| 503 |
const SENSITIVE_SOURCE_QUERY_PREFIXES = ["x-amz-", "x-goog-", "x-oss-"];
|
| 504 |
const TRACKING_SOURCE_QUERY_PREFIXES = ["utm_"];
|
| 505 |
const TRACKING_SOURCE_QUERY_KEYS = new Set(["fbclid", "gclid", "mc_cid", "mc_eid"]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 506 |
|
| 507 |
function cleanOptionalText(value: unknown, maxLength = 256): string | undefined {
|
| 508 |
if (typeof value !== "string") return undefined;
|
|
@@ -641,67 +674,149 @@ function normalizeResearchUsage(input: ResearchUsageInput | undefined): Normaliz
|
|
| 641 |
return Object.keys(usage).length > 0 ? usage : undefined;
|
| 642 |
}
|
| 643 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 644 |
function projectNormalizedResearchSource(
|
| 645 |
-
input:
|
| 646 |
): NormalizedResearchSource {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 647 |
return {
|
| 648 |
-
url
|
| 649 |
-
domain
|
| 650 |
-
...(
|
| 651 |
-
...(
|
| 652 |
-
...(
|
| 653 |
-
? { last_updated_at: input.last_updated_at }
|
| 654 |
-
: {}),
|
| 655 |
};
|
| 656 |
}
|
| 657 |
|
| 658 |
function projectNormalizedResearchUsage(
|
| 659 |
-
input:
|
| 660 |
): NormalizedResearchUsage | undefined {
|
| 661 |
-
if (!input) return undefined;
|
| 662 |
const projected: NormalizedResearchUsage = {
|
| 663 |
-
...(input.input_tokens !== undefined
|
| 664 |
-
|
| 665 |
-
...(input.total_tokens !== undefined ? { total_tokens: input.total_tokens } : {}),
|
| 666 |
-
...(input.reasoning_tokens !== undefined
|
| 667 |
-
? { reasoning_tokens: input.reasoning_tokens }
|
| 668 |
: {}),
|
| 669 |
-
...(input.
|
| 670 |
-
|
| 671 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 672 |
: {}),
|
| 673 |
};
|
| 674 |
return Object.keys(projected).length > 0 ? projected : undefined;
|
| 675 |
}
|
| 676 |
|
| 677 |
function projectNormalizedProviderEvidence(
|
| 678 |
-
input:
|
| 679 |
): NormalizedResearchProviderEvidence {
|
| 680 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 681 |
return {
|
| 682 |
-
schema_version:
|
| 683 |
-
|
| 684 |
-
|
| 685 |
-
|
| 686 |
-
|
| 687 |
-
|
| 688 |
-
|
| 689 |
-
|
| 690 |
-
|
| 691 |
-
...(
|
| 692 |
-
...(
|
| 693 |
-
|
| 694 |
-
|
| 695 |
...(usage ? { usage } : {}),
|
| 696 |
-
...(
|
| 697 |
-
|
| 698 |
-
|
| 699 |
-
|
| 700 |
-
source_count: input.source_count,
|
| 701 |
-
source_list_sha256: input.source_list_sha256,
|
| 702 |
};
|
| 703 |
}
|
| 704 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 705 |
function normalizeProviderEvidence(input: {
|
| 706 |
readonly provider: ResearchProvider;
|
| 707 |
readonly api_surface: NormalizedResearchProviderEvidence["api_surface"];
|
|
@@ -805,10 +920,13 @@ export function compareResearchEvidence(input: {
|
|
| 805 |
}): ResearchEvidenceComparison {
|
| 806 |
const querySha256 = cleanSha256(input.query_sha256);
|
| 807 |
const policySha256 = cleanSha256(input.policy_sha256);
|
| 808 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
| 809 |
.map(projectNormalizedProviderEvidence)
|
| 810 |
.sort((left, right) => left.provider.localeCompare(right.provider));
|
| 811 |
-
const errors: string[] = [];
|
| 812 |
|
| 813 |
if (!SHA256_PATTERN.test(querySha256)) errors.push("expected query_sha256 is not a SHA-256 digest");
|
| 814 |
if (!SHA256_PATTERN.test(policySha256)) errors.push("expected policy_sha256 is not a SHA-256 digest");
|
|
@@ -937,8 +1055,11 @@ export function emitTwoWitnessResearchReceipt(
|
|
| 937 |
const payload = {
|
| 938 |
schema_version: "a11oy.two_witness_research_receipt/v0",
|
| 939 |
live_adapters_enabled: TWO_WITNESS_LIVE_ADAPTERS_ENABLED,
|
|
|
|
| 940 |
signature_state: "UNSIGNED_LOCAL",
|
|
|
|
| 941 |
external_attestation: false,
|
|
|
|
| 942 |
action_authorized: false,
|
| 943 |
query_sha256: comparison.query_sha256,
|
| 944 |
policy_sha256: comparison.policy_sha256,
|
|
|
|
| 503 |
const SENSITIVE_SOURCE_QUERY_PREFIXES = ["x-amz-", "x-goog-", "x-oss-"];
|
| 504 |
const TRACKING_SOURCE_QUERY_PREFIXES = ["utm_"];
|
| 505 |
const TRACKING_SOURCE_QUERY_KEYS = new Set(["fbclid", "gclid", "mc_cid", "mc_eid"]);
|
| 506 |
+
const NORMALIZED_PROVIDER_KEYS = new Set([
|
| 507 |
+
"schema_version",
|
| 508 |
+
"provider",
|
| 509 |
+
"api_surface",
|
| 510 |
+
"tool",
|
| 511 |
+
"status",
|
| 512 |
+
"query_sha256",
|
| 513 |
+
"policy_sha256",
|
| 514 |
+
"response_id",
|
| 515 |
+
"model",
|
| 516 |
+
"http_status",
|
| 517 |
+
"caller_observed_latency_ms",
|
| 518 |
+
"usage",
|
| 519 |
+
"provider_reported_cost_usd",
|
| 520 |
+
"sources",
|
| 521 |
+
"source_count",
|
| 522 |
+
"source_list_sha256",
|
| 523 |
+
]);
|
| 524 |
+
const NORMALIZED_SOURCE_KEYS = new Set([
|
| 525 |
+
"url",
|
| 526 |
+
"domain",
|
| 527 |
+
"title_sha256",
|
| 528 |
+
"published_at",
|
| 529 |
+
"last_updated_at",
|
| 530 |
+
]);
|
| 531 |
+
const NORMALIZED_USAGE_KEYS = new Set([
|
| 532 |
+
"input_tokens",
|
| 533 |
+
"output_tokens",
|
| 534 |
+
"total_tokens",
|
| 535 |
+
"reasoning_tokens",
|
| 536 |
+
"cached_tokens",
|
| 537 |
+
"search_queries",
|
| 538 |
+
]);
|
| 539 |
|
| 540 |
function cleanOptionalText(value: unknown, maxLength = 256): string | undefined {
|
| 541 |
if (typeof value !== "string") return undefined;
|
|
|
|
| 674 |
return Object.keys(usage).length > 0 ? usage : undefined;
|
| 675 |
}
|
| 676 |
|
| 677 |
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
| 678 |
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
| 679 |
+
}
|
| 680 |
+
|
| 681 |
+
function unexpectedKeys(
|
| 682 |
+
value: Record<string, unknown>,
|
| 683 |
+
allowed: ReadonlySet<string>,
|
| 684 |
+
): readonly string[] {
|
| 685 |
+
return Object.keys(value).filter((key) => !allowed.has(key)).sort();
|
| 686 |
+
}
|
| 687 |
+
|
| 688 |
function projectNormalizedResearchSource(
|
| 689 |
+
input: unknown,
|
| 690 |
): NormalizedResearchSource {
|
| 691 |
+
const record = isRecord(input) ? input : {};
|
| 692 |
+
const url = cleanSourceUrl(record.url) ?? "";
|
| 693 |
+
const domain = cleanOptionalText(record.domain, 253)?.toLowerCase() ?? "";
|
| 694 |
+
const titleSha256 = record.title_sha256 === undefined
|
| 695 |
+
? undefined
|
| 696 |
+
: cleanSha256(record.title_sha256);
|
| 697 |
+
const publishedAt = cleanDate(record.published_at);
|
| 698 |
+
const lastUpdatedAt = cleanDate(record.last_updated_at);
|
| 699 |
return {
|
| 700 |
+
url,
|
| 701 |
+
domain,
|
| 702 |
+
...(titleSha256 !== undefined ? { title_sha256: titleSha256 } : {}),
|
| 703 |
+
...(publishedAt !== undefined ? { published_at: publishedAt } : {}),
|
| 704 |
+
...(lastUpdatedAt !== undefined ? { last_updated_at: lastUpdatedAt } : {}),
|
|
|
|
|
|
|
| 705 |
};
|
| 706 |
}
|
| 707 |
|
| 708 |
function projectNormalizedResearchUsage(
|
| 709 |
+
input: unknown,
|
| 710 |
): NormalizedResearchUsage | undefined {
|
| 711 |
+
if (!isRecord(input)) return undefined;
|
| 712 |
const projected: NormalizedResearchUsage = {
|
| 713 |
+
...(cleanNonNegativeInteger(input.input_tokens) !== undefined
|
| 714 |
+
? { input_tokens: cleanNonNegativeInteger(input.input_tokens) }
|
|
|
|
|
|
|
|
|
|
| 715 |
: {}),
|
| 716 |
+
...(cleanNonNegativeInteger(input.output_tokens) !== undefined
|
| 717 |
+
? { output_tokens: cleanNonNegativeInteger(input.output_tokens) }
|
| 718 |
+
: {}),
|
| 719 |
+
...(cleanNonNegativeInteger(input.total_tokens) !== undefined
|
| 720 |
+
? { total_tokens: cleanNonNegativeInteger(input.total_tokens) }
|
| 721 |
+
: {}),
|
| 722 |
+
...(cleanNonNegativeInteger(input.reasoning_tokens) !== undefined
|
| 723 |
+
? { reasoning_tokens: cleanNonNegativeInteger(input.reasoning_tokens) }
|
| 724 |
+
: {}),
|
| 725 |
+
...(cleanNonNegativeInteger(input.cached_tokens) !== undefined
|
| 726 |
+
? { cached_tokens: cleanNonNegativeInteger(input.cached_tokens) }
|
| 727 |
+
: {}),
|
| 728 |
+
...(cleanNonNegativeInteger(input.search_queries) !== undefined
|
| 729 |
+
? { search_queries: cleanNonNegativeInteger(input.search_queries) }
|
| 730 |
: {}),
|
| 731 |
};
|
| 732 |
return Object.keys(projected).length > 0 ? projected : undefined;
|
| 733 |
}
|
| 734 |
|
| 735 |
function projectNormalizedProviderEvidence(
|
| 736 |
+
input: unknown,
|
| 737 |
): NormalizedResearchProviderEvidence {
|
| 738 |
+
const record = isRecord(input) ? input : {};
|
| 739 |
+
const provider = record.provider === "openai" || record.provider === "perplexity"
|
| 740 |
+
? record.provider
|
| 741 |
+
: "invalid" as ResearchProvider;
|
| 742 |
+
const apiSurface = record.api_surface === "openai.responses.web_search"
|
| 743 |
+
|| record.api_surface === "perplexity.search"
|
| 744 |
+
? record.api_surface
|
| 745 |
+
: "invalid" as NormalizedResearchProviderEvidence["api_surface"];
|
| 746 |
+
const tool = record.tool === "web_search" || record.tool === "search"
|
| 747 |
+
? record.tool
|
| 748 |
+
: "invalid" as NormalizedResearchProviderEvidence["tool"];
|
| 749 |
+
const responseId = cleanOptionalText(record.response_id);
|
| 750 |
+
const model = cleanOptionalText(record.model);
|
| 751 |
+
const httpStatus = cleanHttpStatus(record.http_status);
|
| 752 |
+
const latencyMs = cleanNonNegativeNumber(record.caller_observed_latency_ms);
|
| 753 |
+
const usage = projectNormalizedResearchUsage(record.usage);
|
| 754 |
+
const costUsd = cleanNonNegativeNumber(record.provider_reported_cost_usd);
|
| 755 |
+
const suppliedSources = Array.isArray(record.sources) ? record.sources : [];
|
| 756 |
+
const sources = suppliedSources.map(projectNormalizedResearchSource);
|
| 757 |
return {
|
| 758 |
+
schema_version: record.schema_version === "a11oy.research_provider_evidence/v0"
|
| 759 |
+
? record.schema_version
|
| 760 |
+
: "invalid" as NormalizedResearchProviderEvidence["schema_version"],
|
| 761 |
+
provider,
|
| 762 |
+
api_surface: apiSurface,
|
| 763 |
+
tool,
|
| 764 |
+
status: cleanStatus(record.status),
|
| 765 |
+
query_sha256: cleanSha256(record.query_sha256),
|
| 766 |
+
policy_sha256: cleanSha256(record.policy_sha256),
|
| 767 |
+
...(responseId ? { response_id: responseId } : {}),
|
| 768 |
+
...(model ? { model } : {}),
|
| 769 |
+
...(httpStatus !== undefined ? { http_status: httpStatus } : {}),
|
| 770 |
+
...(latencyMs !== undefined ? { caller_observed_latency_ms: latencyMs } : {}),
|
| 771 |
...(usage ? { usage } : {}),
|
| 772 |
+
...(costUsd !== undefined ? { provider_reported_cost_usd: costUsd } : {}),
|
| 773 |
+
sources,
|
| 774 |
+
source_count: cleanNonNegativeInteger(record.source_count) ?? -1,
|
| 775 |
+
source_list_sha256: cleanSha256(record.source_list_sha256),
|
|
|
|
|
|
|
| 776 |
};
|
| 777 |
}
|
| 778 |
|
| 779 |
+
function structuralResearchEvidenceErrors(
|
| 780 |
+
providers: readonly unknown[],
|
| 781 |
+
): readonly string[] {
|
| 782 |
+
const errors: string[] = [];
|
| 783 |
+
for (const [providerIndex, input] of providers.entries()) {
|
| 784 |
+
if (!isRecord(input)) {
|
| 785 |
+
errors.push(`provider ${providerIndex}: expected an object`);
|
| 786 |
+
continue;
|
| 787 |
+
}
|
| 788 |
+
const providerLabel = input.provider === "openai" || input.provider === "perplexity"
|
| 789 |
+
? input.provider
|
| 790 |
+
: `provider ${providerIndex}`;
|
| 791 |
+
for (const key of unexpectedKeys(input, NORMALIZED_PROVIDER_KEYS)) {
|
| 792 |
+
errors.push(`${providerLabel}: unexpected provider field ${key}`);
|
| 793 |
+
}
|
| 794 |
+
if (input.usage !== undefined) {
|
| 795 |
+
if (!isRecord(input.usage)) {
|
| 796 |
+
errors.push(`${providerLabel}: usage must be an object`);
|
| 797 |
+
} else {
|
| 798 |
+
for (const key of unexpectedKeys(input.usage, NORMALIZED_USAGE_KEYS)) {
|
| 799 |
+
errors.push(`${providerLabel}: unexpected usage field ${key}`);
|
| 800 |
+
}
|
| 801 |
+
}
|
| 802 |
+
}
|
| 803 |
+
if (!Array.isArray(input.sources)) {
|
| 804 |
+
errors.push(`${providerLabel}: sources must be an array`);
|
| 805 |
+
continue;
|
| 806 |
+
}
|
| 807 |
+
for (const [sourceIndex, source] of input.sources.entries()) {
|
| 808 |
+
if (!isRecord(source)) {
|
| 809 |
+
errors.push(`${providerLabel}: source ${sourceIndex} must be an object`);
|
| 810 |
+
continue;
|
| 811 |
+
}
|
| 812 |
+
for (const key of unexpectedKeys(source, NORMALIZED_SOURCE_KEYS)) {
|
| 813 |
+
errors.push(`${providerLabel}: source ${sourceIndex} unexpected field ${key}`);
|
| 814 |
+
}
|
| 815 |
+
}
|
| 816 |
+
}
|
| 817 |
+
return errors;
|
| 818 |
+
}
|
| 819 |
+
|
| 820 |
function normalizeProviderEvidence(input: {
|
| 821 |
readonly provider: ResearchProvider;
|
| 822 |
readonly api_surface: NormalizedResearchProviderEvidence["api_surface"];
|
|
|
|
| 920 |
}): ResearchEvidenceComparison {
|
| 921 |
const querySha256 = cleanSha256(input.query_sha256);
|
| 922 |
const policySha256 = cleanSha256(input.policy_sha256);
|
| 923 |
+
const suppliedProviders = Array.isArray(input.providers) ? input.providers : [];
|
| 924 |
+
const errors = Array.isArray(input.providers)
|
| 925 |
+
? [...structuralResearchEvidenceErrors(suppliedProviders)]
|
| 926 |
+
: ["providers must be an array"];
|
| 927 |
+
const providers = suppliedProviders
|
| 928 |
.map(projectNormalizedProviderEvidence)
|
| 929 |
.sort((left, right) => left.provider.localeCompare(right.provider));
|
|
|
|
| 930 |
|
| 931 |
if (!SHA256_PATTERN.test(querySha256)) errors.push("expected query_sha256 is not a SHA-256 digest");
|
| 932 |
if (!SHA256_PATTERN.test(policySha256)) errors.push("expected policy_sha256 is not a SHA-256 digest");
|
|
|
|
| 1055 |
const payload = {
|
| 1056 |
schema_version: "a11oy.two_witness_research_receipt/v0",
|
| 1057 |
live_adapters_enabled: TWO_WITNESS_LIVE_ADAPTERS_ENABLED,
|
| 1058 |
+
evidence_class: "MODELED",
|
| 1059 |
signature_state: "UNSIGNED_LOCAL",
|
| 1060 |
+
external_attestation_state: "EXTERNAL_ATTESTATION_FALSE",
|
| 1061 |
external_attestation: false,
|
| 1062 |
+
action_authorization_state: "ACTION_AUTHORIZED_FALSE",
|
| 1063 |
action_authorized: false,
|
| 1064 |
query_sha256: comparison.query_sha256,
|
| 1065 |
policy_sha256: comparison.policy_sha256,
|
packages/receipt-substrate/src/research_evidence.test.ts
CHANGED
|
@@ -136,7 +136,7 @@ test("vendor-prefixed presigned credentials never enter normalized evidence", ()
|
|
| 136 |
assert.equal(openai.sources[0]?.url, "https://research.example/paper?topic=governance");
|
| 137 |
});
|
| 138 |
|
| 139 |
-
test("comparison
|
| 140 |
const base = fixtures.cases.find((candidate) => candidate.name === "matching");
|
| 141 |
assert.ok(base);
|
| 142 |
const openai = normalizeOpenAIWebSearchResult(base.openai);
|
|
@@ -144,6 +144,10 @@ test("comparison projects provider and source fields through an explicit allowli
|
|
| 144 |
const unsafeOpenAI = {
|
| 145 |
...openai,
|
| 146 |
api_key: "must-not-escape",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
sources: openai.sources.map((source) => ({
|
| 148 |
...source,
|
| 149 |
snippet: "raw snippet must not be receipted",
|
|
@@ -154,15 +158,81 @@ test("comparison projects provider and source fields through an explicit allowli
|
|
| 154 |
policy_sha256: fixtures.policy_sha256,
|
| 155 |
providers: [unsafeOpenAI, perplexity],
|
| 156 |
});
|
| 157 |
-
const
|
| 158 |
-
actor_id: "did:example:projection-test",
|
| 159 |
-
});
|
| 160 |
-
const serialized = JSON.stringify(receipt.envelope);
|
| 161 |
|
| 162 |
assert.equal(serialized.includes("must-not-escape"), false);
|
|
|
|
| 163 |
assert.equal(serialized.includes("raw snippet must not be receipted"), false);
|
| 164 |
assert.equal(serialized.includes("\"api_key\""), false);
|
|
|
|
| 165 |
assert.equal(serialized.includes("\"snippet\""), false);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 166 |
});
|
| 167 |
|
| 168 |
test("live adapters remain disabled and the public label vocabulary excludes a truth claim", () => {
|
|
|
|
| 136 |
assert.equal(openai.sources[0]?.url, "https://research.example/paper?topic=governance");
|
| 137 |
});
|
| 138 |
|
| 139 |
+
test("comparison rejects undeclared provider, usage, and source fields", () => {
|
| 140 |
const base = fixtures.cases.find((candidate) => candidate.name === "matching");
|
| 141 |
assert.ok(base);
|
| 142 |
const openai = normalizeOpenAIWebSearchResult(base.openai);
|
|
|
|
| 144 |
const unsafeOpenAI = {
|
| 145 |
...openai,
|
| 146 |
api_key: "must-not-escape",
|
| 147 |
+
usage: {
|
| 148 |
+
...(openai.usage ?? {}),
|
| 149 |
+
response_body: "raw usage must not be receipted",
|
| 150 |
+
},
|
| 151 |
sources: openai.sources.map((source) => ({
|
| 152 |
...source,
|
| 153 |
snippet: "raw snippet must not be receipted",
|
|
|
|
| 158 |
policy_sha256: fixtures.policy_sha256,
|
| 159 |
providers: [unsafeOpenAI, perplexity],
|
| 160 |
});
|
| 161 |
+
const serialized = JSON.stringify(comparison);
|
|
|
|
|
|
|
|
|
|
| 162 |
|
| 163 |
assert.equal(serialized.includes("must-not-escape"), false);
|
| 164 |
+
assert.equal(serialized.includes("raw usage must not be receipted"), false);
|
| 165 |
assert.equal(serialized.includes("raw snippet must not be receipted"), false);
|
| 166 |
assert.equal(serialized.includes("\"api_key\""), false);
|
| 167 |
+
assert.equal(serialized.includes("\"response_body\""), false);
|
| 168 |
assert.equal(serialized.includes("\"snippet\""), false);
|
| 169 |
+
assert.equal(comparison.integrity_valid, false);
|
| 170 |
+
assert.ok(
|
| 171 |
+
comparison.integrity_errors.includes("openai: unexpected provider field api_key"),
|
| 172 |
+
);
|
| 173 |
+
assert.ok(
|
| 174 |
+
comparison.integrity_errors.includes("openai: unexpected usage field response_body"),
|
| 175 |
+
);
|
| 176 |
+
assert.ok(
|
| 177 |
+
comparison.integrity_errors.includes("openai: source 0 unexpected field snippet"),
|
| 178 |
+
);
|
| 179 |
+
assert.throws(
|
| 180 |
+
() => emitTwoWitnessResearchReceipt(comparison, {
|
| 181 |
+
actor_id: "did:example:projection-test",
|
| 182 |
+
}),
|
| 183 |
+
/comparison verification failed/,
|
| 184 |
+
);
|
| 185 |
+
});
|
| 186 |
+
|
| 187 |
+
test("direct comparison rebuilds credential-bearing allowed fields before receipt emission", () => {
|
| 188 |
+
const base = fixtures.cases.find((candidate) => candidate.name === "matching");
|
| 189 |
+
assert.ok(base);
|
| 190 |
+
const openai = normalizeOpenAIWebSearchResult(base.openai);
|
| 191 |
+
const perplexity = normalizePerplexitySearchResult(base.perplexity);
|
| 192 |
+
const unsafeOpenAI = {
|
| 193 |
+
...openai,
|
| 194 |
+
response_id: { api_key: "response-id-secret-must-not-escape" },
|
| 195 |
+
model: { snippet: "model-secret-must-not-escape" },
|
| 196 |
+
sources: openai.sources.map((source, index) => (
|
| 197 |
+
index === 0
|
| 198 |
+
? {
|
| 199 |
+
...source,
|
| 200 |
+
url: `${source.url}?X-Amz-Credential=source-secret-must-not-escape`,
|
| 201 |
+
}
|
| 202 |
+
: source
|
| 203 |
+
)),
|
| 204 |
+
} as unknown as NormalizedResearchProviderEvidence;
|
| 205 |
+
const comparison = compareResearchEvidence({
|
| 206 |
+
query_sha256: fixtures.query_sha256,
|
| 207 |
+
policy_sha256: fixtures.policy_sha256,
|
| 208 |
+
providers: [unsafeOpenAI, perplexity],
|
| 209 |
+
});
|
| 210 |
+
const receipt = emitTwoWitnessResearchReceipt(comparison, {
|
| 211 |
+
actor_id: "did:example:allowed-field-negative",
|
| 212 |
+
});
|
| 213 |
+
const serialized = JSON.stringify(receipt.envelope);
|
| 214 |
+
const payload = receipt.envelope.payload as Record<string, unknown>;
|
| 215 |
+
|
| 216 |
+
for (const forbidden of [
|
| 217 |
+
"response-id-secret-must-not-escape",
|
| 218 |
+
"model-secret-must-not-escape",
|
| 219 |
+
"source-secret-must-not-escape",
|
| 220 |
+
]) {
|
| 221 |
+
assert.equal(serialized.includes(forbidden), false);
|
| 222 |
+
}
|
| 223 |
+
assert.equal(comparison.providers[0]?.response_id, undefined);
|
| 224 |
+
assert.equal(comparison.providers[0]?.model, undefined);
|
| 225 |
+
assert.equal(
|
| 226 |
+
comparison.providers[0]?.sources[0]?.url,
|
| 227 |
+
"https://example.com/research/report",
|
| 228 |
+
);
|
| 229 |
+
assert.equal(comparison.integrity_valid, true);
|
| 230 |
+
assert.equal(payload.evidence_class, "MODELED");
|
| 231 |
+
assert.equal(payload.signature_state, "UNSIGNED_LOCAL");
|
| 232 |
+
assert.equal(payload.external_attestation_state, "EXTERNAL_ATTESTATION_FALSE");
|
| 233 |
+
assert.equal(payload.external_attestation, false);
|
| 234 |
+
assert.equal(payload.action_authorization_state, "ACTION_AUTHORIZED_FALSE");
|
| 235 |
+
assert.equal(payload.action_authorized, false);
|
| 236 |
});
|
| 237 |
|
| 238 |
test("live adapters remain disabled and the public label vocabulary excludes a truth claim", () => {
|