tamperward 2.38.0 → 2.38.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli/index.js +40 -5
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -21899,6 +21899,7 @@ var init_report2 = __esm({
21899
21899
 
21900
21900
  // src/research/bundle.ts
21901
21901
  import { createHash as createHash17 } from "node:crypto";
21902
+ import { isDeepStrictEqual } from "node:util";
21902
21903
  import { gzipSync, gunzipSync } from "node:zlib";
21903
21904
  import { mkdirSync as mkdirSync13, readFileSync as readFileSync26, writeFileSync as writeFileSync15 } from "node:fs";
21904
21905
  import { dirname as dirname14, resolve as resolve19 } from "node:path";
@@ -22039,6 +22040,15 @@ function validateResearchBundle(path) {
22039
22040
  }
22040
22041
  const parsedSummary = summary;
22041
22042
  if (parsedSummary.document !== "summary" || parsedSummary.command !== "research") throw new ResearchError("research bundle summary is not a research summary");
22043
+ for (const key2 of Object.keys(p)) {
22044
+ if (!PROVENANCE_KEYS.has(key2)) throw new ResearchError(`research bundle provenance contains unknown field ${key2}`);
22045
+ }
22046
+ if (p.manifest_sha256 !== parsedSummary.manifest_sha256) throw new ResearchError("research bundle provenance manifest hash disagrees with summary");
22047
+ if (!isDeepStrictEqual(p.adapter, parsedSummary.adapter)) throw new ResearchError("research bundle provenance adapter disagrees with summary");
22048
+ if (p.model !== parsedSummary.model) throw new ResearchError("research bundle provenance model disagrees with summary");
22049
+ if (p.tamperward_version !== parsedSummary.tamperward_version) throw new ResearchError("research bundle provenance TamperWard version disagrees with summary");
22050
+ if (!isDeepStrictEqual(p.agent_argv, parsedSummary.agent_argv)) throw new ResearchError("research bundle provenance agent argv disagrees with summary");
22051
+ if (p.agent_budget !== parsedSummary.agent_budget) throw new ResearchError("research bundle provenance agent budget disagrees with summary");
22042
22052
  const records = [];
22043
22053
  for (const [name, bytes] of files) {
22044
22054
  if (!PAIR_ENTRY_RE.test(name)) continue;
@@ -22054,9 +22064,9 @@ function validateResearchBundle(path) {
22054
22064
  }
22055
22065
  if (records.length === 0) throw new ResearchError("research bundle contains no pair records");
22056
22066
  const derived = summarizeRecords(records);
22067
+ if (p.records !== records.length) throw new ResearchError("research bundle provenance record count disagrees with pair records");
22057
22068
  if (JSON.stringify(derived) !== JSON.stringify(summary)) throw new ResearchError("research bundle summary does not match its pair records");
22058
22069
  if (report2.toString("utf8") !== renderResearchReport(derived)) throw new ResearchError("research bundle report does not match its summary");
22059
- if (p.manifest_sha256 !== parsedSummary.manifest_sha256) throw new ResearchError("research bundle manifest hash disagrees with summary");
22060
22070
  if (!p.protocol || p.protocol !== "research-bundle-v1") throw new ResearchError("research bundle protocol is missing or unsupported");
22061
22071
  return { records: records.length, manifest_sha256: String(p.manifest_sha256) };
22062
22072
  }
@@ -22115,7 +22125,7 @@ function runResearchBundle(opts) {
22115
22125
  throw e;
22116
22126
  }
22117
22127
  }
22118
- var BLOCK, BUNDLE_VERSION, TOP_LEVEL_ENTRIES, PAIR_ENTRY_RE, TAR_NAME_MAX, NAME_FIELD, TYPE_OFFSET, PREFIX_OFFSET, PREFIX_FIELD;
22128
+ var BLOCK, BUNDLE_VERSION, PROVENANCE_KEYS, TOP_LEVEL_ENTRIES, PAIR_ENTRY_RE, TAR_NAME_MAX, NAME_FIELD, TYPE_OFFSET, PREFIX_OFFSET, PREFIX_FIELD;
22119
22129
  var init_bundle = __esm({
22120
22130
  "src/research/bundle.ts"() {
22121
22131
  "use strict";
@@ -22127,6 +22137,20 @@ var init_bundle = __esm({
22127
22137
  init_summarize();
22128
22138
  BLOCK = 512;
22129
22139
  BUNDLE_VERSION = 1;
22140
+ PROVENANCE_KEYS = /* @__PURE__ */ new Set([
22141
+ "bundle_schema_version",
22142
+ "protocol",
22143
+ "schema_version",
22144
+ "manifest_sha256",
22145
+ "adapter",
22146
+ "model",
22147
+ "tamperward_version",
22148
+ "agent_argv",
22149
+ "agent_budget",
22150
+ "records",
22151
+ "manifest_included",
22152
+ "prompts_included"
22153
+ ]);
22130
22154
  TOP_LEVEL_ENTRIES = /* @__PURE__ */ new Set(["summary.json", "report.txt", "provenance.json", "manifest.json"]);
22131
22155
  PAIR_ENTRY_RE = /^ledger\/pairs\/[^/]+\.json$/;
22132
22156
  TAR_NAME_MAX = 99;
@@ -23808,8 +23832,10 @@ function assessCapabilities(caps, evidence) {
23808
23832
  }
23809
23833
  function aggregateInLoop(assessments) {
23810
23834
  const byId = new Map(assessments.map((a) => [a.id, a]));
23811
- const required = REQUIRED_IN_LOOP_IDS.map((id) => byId.get(id)).filter((a) => a !== void 0);
23812
- const anyDangerous = required.some((a) => a.state === "FAIL-OPEN" || a.state === "INCONCLUSIVE");
23835
+ const required = REQUIRED_IN_LOOP_IDS.map((id) => byId.get(id));
23836
+ const presentRequired = required.filter((a) => a !== void 0);
23837
+ const completeRequiredSet = required.every((a) => a !== void 0) && REQUIRED_IN_LOOP_IDS.every((id) => assessments.filter((a) => a.id === id).length === 1);
23838
+ const anyDangerous = presentRequired.some((a) => a.state === "FAIL-OPEN" || a.state === "INCONCLUSIVE");
23813
23839
  const present = (id) => {
23814
23840
  const s = byId.get(id)?.state;
23815
23841
  return s === "PROVEN" || s === "PARTIAL";
@@ -23817,7 +23843,7 @@ function aggregateInLoop(assessments) {
23817
23843
  const anyPreDenyPresent = PRE_DENY_IDS.some(present);
23818
23844
  const endOfTurnPresent = present("end-of-turn");
23819
23845
  if (!anyPreDenyPresent && !endOfTurnPresent) return "NONE";
23820
- if (!anyDangerous && required.every((a) => a.state === "PROVEN")) return "FULL";
23846
+ if (!anyDangerous && completeRequiredSet && presentRequired.every((a) => a.state === "PROVEN")) return "FULL";
23821
23847
  return "PARTIAL";
23822
23848
  }
23823
23849
  function sha16(s) {
@@ -24021,11 +24047,20 @@ function validateStoredReport(value) {
24021
24047
  if (!source || typeof detail !== "string") return { reason: "invalid capability evidence" };
24022
24048
  capabilities.push({ id, state, evidence: { source, detail } });
24023
24049
  }
24050
+ const capabilityIds = capabilities.map((c) => c.id);
24051
+ const capabilitySet = new Set(capabilityIds);
24052
+ const testedSet = new Set(testedCapabilities);
24053
+ if (capabilityIds.length !== capabilitySet.size || testedCapabilities.length !== testedSet.size || capabilitySet.size !== testedSet.size || capabilityIds.some((id) => !testedSet.has(id)) || testedCapabilities.some((id) => !capabilitySet.has(id))) {
24054
+ return { reason: "capabilities do not exactly match tested_capabilities" };
24055
+ }
24024
24056
  const inLoop = value.in_loop_protection;
24025
24057
  if (inLoop !== "FULL" && inLoop !== "PARTIAL" && inLoop !== "NONE") return { reason: "invalid in_loop_protection" };
24026
24058
  if (value.final_authority !== FINAL_AUTHORITY) return { reason: "invalid final_authority" };
24027
24059
  const note = value.note;
24028
24060
  if (typeof note !== "string") return { reason: "invalid note" };
24061
+ if (!recorded && (capabilities.length > 0 || inLoop !== "NONE")) {
24062
+ return { reason: "unrecorded report carries capability data" };
24063
+ }
24029
24064
  const base = {
24030
24065
  runtime: { id: rId, label: rLabel, version: rVersion },
24031
24066
  tamperward: { version: twVersion, commit: twCommit },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tamperward",
3
- "version": "2.38.0",
3
+ "version": "2.38.2",
4
4
  "description": "The deterministic agent-integrity gate. One ruleset, evaluated on the actual diff/commands as a verdict, enforced everywhere a change can be made.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "hexrift",