tamperward 2.29.13 → 2.29.14

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 +66 -16
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -17883,6 +17883,22 @@ function parseStraceFileAccess(raw) {
17883
17883
  }
17884
17884
  return out3;
17885
17885
  }
17886
+ function tracerDenialDiagnostic(stderr) {
17887
+ for (const raw of stderr.split("\n")) {
17888
+ const line = raw.trim();
17889
+ if (line.startsWith("strace:") && TRACER_DENIAL_RE.test(line)) return line;
17890
+ }
17891
+ return null;
17892
+ }
17893
+ function classifyTraceRun(r) {
17894
+ if (r.spawnError) return { kind: "tracer-error", diagnostic: `strace could not be launched: ${r.spawnError}` };
17895
+ const denial = tracerDenialDiagnostic(r.stderr);
17896
+ if (denial) return { kind: "tracer-error", diagnostic: denial };
17897
+ if (r.signal) return { kind: "tracer-error", diagnostic: `strace was killed by ${r.signal} before the trace completed` };
17898
+ if (r.traceFileCount === 0) return { kind: "tracer-error", diagnostic: "strace produced no trace files; the tracer did not attach (ptrace may be denied)" };
17899
+ if (r.accessCount === 0) return { kind: "tracer-error", diagnostic: "strace recorded no file syscalls; the tracer attached to nothing (ptrace may be denied)" };
17900
+ return { kind: "ok", exit: r.status ?? 1 };
17901
+ }
17886
17902
  function inside3(root, path) {
17887
17903
  const rel = relative6(resolve11(root), resolve11(path));
17888
17904
  return rel === "" || rel !== ".." && !rel.startsWith(`..${sep4}`) && !isAbsolute12(rel);
@@ -18009,11 +18025,9 @@ function rewriteTraceRoot(accesses, actualRoot) {
18009
18025
  return { ...item, path: join22(TRACE_ROOT, ...rel.split("/")) };
18010
18026
  });
18011
18027
  }
18012
- function runOneTrace(cwd, base, command, budget) {
18013
- const root = mkdtempSync5(join22(tmpdir6(), "tw-trace-base-"));
18028
+ function straceOnce(root, command, budget) {
18014
18029
  const traceDir = mkdtempSync5(join22(tmpdir6(), "tw-trace-log-"));
18015
18030
  try {
18016
- materializeBase(base, cwd, root);
18017
18031
  const prefix = join22(traceDir, "trace");
18018
18032
  const traced = spawnSync5(
18019
18033
  "strace",
@@ -18042,21 +18056,40 @@ function runOneTrace(cwd, base, command, budget) {
18042
18056
  maxBuffer: 64 * 1024 * 1024
18043
18057
  }
18044
18058
  );
18045
- if (traced.error) throw traced.error;
18046
- const logs = traceFiles(prefix);
18047
- if (logs.length === 0) {
18048
- throw new Error("strace produced no trace files");
18049
- }
18059
+ const logs = traced.error ? [] : traceFiles(prefix);
18050
18060
  const accesses = logs.flatMap((path) => parseStraceFileAccess(readFileSync20(path, "utf8")));
18051
- return {
18052
- exit: traced.status ?? 1,
18053
- accesses: rewriteTraceRoot(accesses, root)
18061
+ const raw = {
18062
+ spawnError: traced.error ? traced.error.message : null,
18063
+ status: traced.status,
18064
+ signal: traced.signal ?? null,
18065
+ stderr: String(traced.stderr ?? "").slice(0, 8192),
18066
+ traceFileCount: logs.length,
18067
+ accessCount: accesses.length
18054
18068
  };
18069
+ return { raw, accesses };
18055
18070
  } finally {
18056
- rmSync9(root, { recursive: true, force: true });
18057
18071
  rmSync9(traceDir, { recursive: true, force: true });
18058
18072
  }
18059
18073
  }
18074
+ function runOneTrace(cwd, base, command, budget) {
18075
+ const root = mkdtempSync5(join22(tmpdir6(), "tw-trace-base-"));
18076
+ try {
18077
+ materializeBase(base, cwd, root);
18078
+ const { raw, accesses } = straceOnce(root, command, budget);
18079
+ return { outcome: classifyTraceRun(raw), accesses: rewriteTraceRoot(accesses, root) };
18080
+ } finally {
18081
+ rmSync9(root, { recursive: true, force: true });
18082
+ }
18083
+ }
18084
+ function tracerPreflight(budget = 10) {
18085
+ const probeRoot = mkdtempSync5(join22(tmpdir6(), "tw-trace-probe-"));
18086
+ try {
18087
+ const { raw } = straceOnce(probeRoot, "true", Math.max(1, Math.min(budget, 10)));
18088
+ return classifyTraceRun(raw);
18089
+ } finally {
18090
+ rmSync9(probeRoot, { recursive: true, force: true });
18091
+ }
18092
+ }
18060
18093
  function renderText2(report2) {
18061
18094
  const out3 = (s = "") => void process.stdout.write(s + "\n");
18062
18095
  out3("tamperward trace-verify \u2014 ADVISORY observation, not an enforcement verdict");
@@ -18143,6 +18176,15 @@ function runTraceVerify(opts = {}) {
18143
18176
  `);
18144
18177
  return 2;
18145
18178
  }
18179
+ const preflight = tracerPreflight(budget);
18180
+ if (preflight.kind === "tracer-error") {
18181
+ process.stderr.write(
18182
+ `tamperward trace-verify: the tracer cannot observe in this environment, so no verifier input can be discovered \u2014 ${preflight.diagnostic}.
18183
+ This is a tracing/tooling limitation (e.g. ptrace denied by seccomp or Yama), not a verifier result.
18184
+ `
18185
+ );
18186
+ return 2;
18187
+ }
18146
18188
  const atBase = trackedAt(base, cwd);
18147
18189
  const tracked = new Set(atBase);
18148
18190
  const covered = verifierCoveredInputs(command, atBase, policy);
@@ -18150,9 +18192,16 @@ function runTraceVerify(opts = {}) {
18150
18192
  const runExits = [];
18151
18193
  try {
18152
18194
  for (let i = 0; i < runs; i++) {
18153
- const result = runOneTrace(cwd, base, command, budget);
18154
- observed.push(result.accesses);
18155
- runExits.push(result.exit);
18195
+ const { outcome, accesses } = runOneTrace(cwd, base, command, budget);
18196
+ if (outcome.kind === "tracer-error") {
18197
+ process.stderr.write(
18198
+ `tamperward trace-verify: tracing failed during run ${i + 1}/${runs} \u2014 ${outcome.diagnostic}. No advisory report emitted.
18199
+ `
18200
+ );
18201
+ return 2;
18202
+ }
18203
+ observed.push(accesses);
18204
+ runExits.push(outcome.exit);
18156
18205
  }
18157
18206
  } catch (e) {
18158
18207
  process.stderr.write(`tamperward trace-verify: tracing failed (${e instanceof Error ? e.message : String(e)}).
@@ -18199,7 +18248,7 @@ function parseTraceVerify(args) {
18199
18248
  }
18200
18249
  return out3;
18201
18250
  }
18202
- var TRACE_ROOT, CONFIG_BASENAMES;
18251
+ var TRACE_ROOT, TRACER_DENIAL_RE, CONFIG_BASENAMES;
18203
18252
  var init_trace_verify = __esm({
18204
18253
  "src/cli/trace-verify.ts"() {
18205
18254
  "use strict";
@@ -18209,6 +18258,7 @@ var init_trace_verify = __esm({
18209
18258
  init_policy_load();
18210
18259
  init_verify();
18211
18260
  TRACE_ROOT = "/__tamperward_trusted_base__";
18261
+ TRACER_DENIAL_RE = /\b(?:PTRACE_[A-Z_]+|ptrace|seccomp|no[-_ ]new[-_ ]privs|yama)\b|Operation not permitted|Permission denied/i;
18212
18262
  CONFIG_BASENAMES = /* @__PURE__ */ new Set([
18213
18263
  "package.json",
18214
18264
  "pytest.ini",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tamperward",
3
- "version": "2.29.13",
3
+ "version": "2.29.14",
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",