@codai/axiom-mcp 2.0.0 → 2.2.0

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.
package/dist/gate-lazy.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import { realpath } from "node:fs";
2
2
  import * as fs$1 from "node:fs/promises";
3
- import { readFile } from "node:fs/promises";
3
+ import { lstat, readFile } from "node:fs/promises";
4
4
  import { cpus, homedir } from "node:os";
5
5
  import * as path from "node:path";
6
6
  import { parseArgs, promisify } from "node:util";
7
+ import "node:crypto";
7
8
  import "node:child_process";
8
9
  var __create = Object.create;
9
10
  var __defProp = Object.defineProperty;
@@ -27,6 +28,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
28
  enumerable: true
28
29
  }) : target, mod));
29
30
  new TextEncoder();
31
+ Buffer.from("302a300506032b6570032100", "hex");
32
+ Buffer.from("302e020100300506032b657004220420", "hex");
30
33
  function getEnumValues(entries) {
31
34
  const numericValues = Object.values(entries).filter((v) => typeof v === "number");
32
35
  return Object.entries(entries).filter(([k, _]) => numericValues.indexOf(+k) === -1).map(([_, v]) => v);
@@ -5677,13 +5680,30 @@ const ERROR_CODES = [
5677
5680
  "ERR_PROVIDER_FAILED",
5678
5681
  "ERR_GUARD_TIMEOUT",
5679
5682
  "ERR_GUARD_OUTPUT",
5683
+ "ERR_FACT_DISABLED",
5684
+ "ERR_SIGNATURE_MISSING",
5685
+ "ERR_SIGNATURE_INVALID",
5686
+ "ERR_TRUST_STATE_CORRUPT",
5687
+ "ERR_ROLLBACK",
5688
+ "ERR_EMITTER_UNKNOWN",
5689
+ "ERR_TEMPLATE_UNKNOWN",
5690
+ "ERR_TEMPLATE_PARAMS",
5691
+ "ERR_PATCH_FORMAT",
5692
+ "ERR_PATCH_PREIMAGE",
5693
+ "ERR_PATCH_NO_MATCH",
5680
5694
  "ERR_GIT_NOT_FOUND",
5681
5695
  "ERR_GIT_NOT_REPO",
5682
5696
  "ERR_GIT_DIRTY",
5683
5697
  "ERR_GIT_BRANCH_EXISTS",
5684
5698
  "ERR_GIT_BRANCH_INVALID",
5685
5699
  "ERR_GIT_FAILED",
5700
+ "ERR_TASK_NOT_FOUND",
5701
+ "ERR_TASK_CANCELLED",
5702
+ "ERR_PLAN_SESSION_STATE",
5686
5703
  "ERR_REF_OFFLINE",
5704
+ "ERR_NET_DISABLED",
5705
+ "ERR_NET_DENIED",
5706
+ "ERR_NET_FAILED",
5687
5707
  "ERR_NOT_CANONICAL",
5688
5708
  "ERR_UNSUPPORTED_OP",
5689
5709
  "ERR_INTERNAL"
@@ -5787,7 +5807,12 @@ object({
5787
5807
  findings: array(FindingSchema),
5788
5808
  factsDigest: DigestRefSchema,
5789
5809
  durationMs: int().nonnegative(),
5790
- providers: array(ProviderStatusSchema)
5810
+ providers: array(ProviderStatusSchema),
5811
+ preImage: _enum([
5812
+ "verified",
5813
+ "drifted",
5814
+ "unverified"
5815
+ ]).optional()
5791
5816
  }).strict();
5792
5817
  const ApiVersionSchema = literal("axiom.dev/v2");
5793
5818
  const INLINE_CONTENT_MAX = 262144;
@@ -5826,7 +5851,17 @@ const PlanArtifactSourceSchema = discriminatedUnion("type", [
5826
5851
  emitter: string().min(1),
5827
5852
  template: string().min(1),
5828
5853
  params: record(string(), json()).default({})
5829
- }).strict().describe("v2.1")
5854
+ }).strict().describe("template source rendered by a registered emitter at compile time"),
5855
+ object({
5856
+ type: literal("patch"),
5857
+ format: _enum([
5858
+ "unified",
5859
+ "v4a",
5860
+ "search-replace"
5861
+ ]),
5862
+ preImage: union([DigestRefSchema, literal("absent")]),
5863
+ body: string().max(INLINE_CONTENT_MAX)
5864
+ }).strict().describe("patch applied at compile time to the pre-image named by its digest")
5830
5865
  ]);
5831
5866
  const PlanArtifactSchema = object({
5832
5867
  path: RelPathSchema,
@@ -5856,6 +5891,7 @@ object({
5856
5891
  capabilities: array(CapabilitySchema).default([]),
5857
5892
  artifacts: array(PlanArtifactSchema).min(1).max(2e3),
5858
5893
  checks: array(CheckRefSchema).default([]),
5894
+ counter: int().nonnegative().optional(),
5859
5895
  metadata: record(string(), json()).default({})
5860
5896
  }).strict();
5861
5897
  const ErrorCodeSchema = _enum(ERROR_CODES);
@@ -5900,6 +5936,7 @@ object({
5900
5936
  diff: string().optional(),
5901
5937
  journal: string().optional(),
5902
5938
  git: GitResultSchema.optional(),
5939
+ drifted: array(RelPathSchema).optional(),
5903
5940
  error: ApplyErrorSchema.optional()
5904
5941
  }).strict().superRefine((r, ctx) => {
5905
5942
  if (r.status === "failed" && r.error === void 0) ctx.addIssue({
@@ -5960,7 +5997,8 @@ const ManifestArtifactSchema = object({
5960
5997
  "inline",
5961
5998
  "template",
5962
5999
  "cas",
5963
- "ref"
6000
+ "ref",
6001
+ "patch"
5964
6002
  ]).optional()
5965
6003
  }).strict().superRefine((a, ctx) => {
5966
6004
  if (a.op !== "delete" && a.digest === void 0) ctx.addIssue({
@@ -5974,6 +6012,10 @@ const ToolchainSchema = object({
5974
6012
  axiom: string().min(1),
5975
6013
  emitters: record(string(), string())
5976
6014
  }).strict();
6015
+ const PreImageEntrySchema = object({
6016
+ path: RelPathSchema,
6017
+ sha256: union([Sha256HexSchema, literal("absent")])
6018
+ }).strict();
5977
6019
  const ManifestBodySchema = object({
5978
6020
  apiVersion: ApiVersionSchema,
5979
6021
  kind: literal("Manifest"),
@@ -5982,8 +6024,16 @@ const ManifestBodySchema = object({
5982
6024
  planDigest: DigestRefSchema,
5983
6025
  artifacts: array(ManifestArtifactSchema).min(1).max(2e3),
5984
6026
  checks: array(CheckRefSchema),
5985
- toolchain: ToolchainSchema
6027
+ toolchain: ToolchainSchema,
6028
+ counter: int().nonnegative().optional(),
6029
+ preImage: array(PreImageEntrySchema).optional()
5986
6030
  }).strict().superRefine((m, ctx) => {
6031
+ if (m.preImage !== void 0 && !isSortedUnique(m.preImage.map((p) => p.path))) ctx.addIssue({
6032
+ code: "custom",
6033
+ path: ["preImage"],
6034
+ message: "preImage must be sorted by path (UTF-8 byte order) and unique",
6035
+ params: { code: "ERR_NOT_CANONICAL" }
6036
+ });
5987
6037
  if (!isSortedUnique(m.artifacts.map((a) => a.path))) ctx.addIssue({
5988
6038
  code: "custom",
5989
6039
  path: ["artifacts"],
@@ -6023,11 +6073,42 @@ const DsseEnvelopeSchema = object({
6023
6073
  sig: base64()
6024
6074
  }).strict())
6025
6075
  }).strict();
6076
+ const AXIOM_MANIFEST_PAYLOAD_TYPE = "application/vnd.axiom.manifest+json";
6077
+ const AXIOM_MANIFEST_BOUND_PAYLOAD_TYPE = "application/vnd.axiom.manifest-bound+json";
6078
+ const RootIdSchema = string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9._:/@-]*$/, "rootId: letters, digits, . _ : / @ - only");
6079
+ const ManifestSignatureSchema = object({
6080
+ payloadType: union([literal(AXIOM_MANIFEST_PAYLOAD_TYPE), literal(AXIOM_MANIFEST_BOUND_PAYLOAD_TYPE)]),
6081
+ payload: base64(),
6082
+ signatures: array(object({
6083
+ keyid: string().optional(),
6084
+ sig: base64()
6085
+ }).strict()).min(1)
6086
+ }).strict();
6087
+ const TrustedKeySchema = object({
6088
+ keyid: string().regex(/^[0-9a-f]{64}$/),
6089
+ alg: literal("ed25519"),
6090
+ publicKey: base64(),
6091
+ name: string().min(1).max(200).optional(),
6092
+ notBefore: int().nonnegative().optional()
6093
+ }).strict();
6094
+ object({
6095
+ version: literal(1),
6096
+ keys: array(TrustedKeySchema),
6097
+ minCounter: int().nonnegative().optional(),
6098
+ rootId: RootIdSchema.optional()
6099
+ }).strict();
6100
+ object({
6101
+ version: literal(1),
6102
+ lastCounter: int().nonnegative(),
6103
+ manifestDigest: DigestRefSchema.optional(),
6104
+ mac: string().regex(/^[0-9a-f]{64}$/).optional()
6105
+ }).strict();
6026
6106
  object({
6027
6107
  manifest: ManifestBodySchema,
6028
6108
  manifestDigest: DigestRefSchema,
6029
6109
  attestation: InTotoStatementLooseSchema.optional(),
6030
6110
  envelope: DsseEnvelopeSchema.optional(),
6111
+ signatures: array(ManifestSignatureSchema).optional(),
6031
6112
  blobs: record(DigestRefSchema, BlobSchema).default({})
6032
6113
  }).strict().superRefine((b, ctx) => {
6033
6114
  let total = 0;
@@ -6062,6 +6143,28 @@ object({
6062
6143
  limits: ProfileLimitsSchema.default({}),
6063
6144
  facts: ProfileFactsSchema.prefault({})
6064
6145
  }).strict();
6146
+ const RepoSnapshotEntryKindSchema = _enum(["file", "symlink"]);
6147
+ const RepoSnapshotBodySchema = object({
6148
+ files: array(object({
6149
+ path: RelPathSchema,
6150
+ bytes: int().nonnegative(),
6151
+ sha256: Sha256HexSchema.optional(),
6152
+ mode: ArtifactModeSchema,
6153
+ kind: RepoSnapshotEntryKindSchema
6154
+ }).strict()),
6155
+ truncated: boolean(),
6156
+ counts: object({
6157
+ files: int().nonnegative(),
6158
+ bytes: int().nonnegative()
6159
+ }).strict()
6160
+ }).strict();
6161
+ object({
6162
+ apiVersion: ApiVersionSchema,
6163
+ kind: literal("RepoSnapshot"),
6164
+ root: object({ kind: literal("relative") }).strict(),
6165
+ snapshotDigest: DigestRefSchema,
6166
+ body: RepoSnapshotBodySchema
6167
+ }).strict();
6065
6168
  var Diff = class {
6066
6169
  diff(oldStr, newStr, options = {}) {
6067
6170
  let callback;
@@ -8228,9 +8331,14 @@ var import_picomatch = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((
8228
8331
  function definePredicate(p) {
8229
8332
  return p;
8230
8333
  }
8334
+ function escapeRouteGroups(glob) {
8335
+ return glob.replace(/(^|[^\\@!?*+])\(([^()|*?[\]{}\\]+)\)/g, (_m, pre, inner) => {
8336
+ return `${pre}\\(${inner}\\)`;
8337
+ });
8338
+ }
8231
8339
  function globMatcher(globs, whenEmpty) {
8232
8340
  if (globs === void 0 || globs.length === 0) return () => whenEmpty;
8233
- const m = (0, import_picomatch.default)([...globs], { dot: true });
8341
+ const m = (0, import_picomatch.default)(globs.map(escapeRouteGroups), { dot: true });
8234
8342
  return (p) => m(p);
8235
8343
  }
8236
8344
  function finding(f) {
@@ -8244,60 +8352,165 @@ function finding(f) {
8244
8352
  if (f.path !== void 0) out.path = f.path;
8245
8353
  return out;
8246
8354
  }
8355
+ object({
8356
+ expression: string().min(1).max(4096),
8357
+ message: string().max(2e3).optional(),
8358
+ severity: _enum([
8359
+ "error",
8360
+ "warn",
8361
+ "info"
8362
+ ]).optional()
8363
+ }).strict();
8364
+ object({
8365
+ policies: string().min(1).max(65536),
8366
+ mode: _enum(["forbid", "permit"]).default("forbid"),
8367
+ message: string().max(2e3).optional(),
8368
+ severity: _enum([
8369
+ "error",
8370
+ "warn",
8371
+ "info"
8372
+ ]).optional()
8373
+ }).strict();
8374
+ function luhnValid(digits) {
8375
+ let sum = 0;
8376
+ let double = false;
8377
+ for (let i = digits.length - 1; i >= 0; i--) {
8378
+ let d = digits.charCodeAt(i) - 48;
8379
+ if (d < 0 || d > 9) return false;
8380
+ if (double) {
8381
+ d *= 2;
8382
+ if (d > 9) d -= 9;
8383
+ }
8384
+ sum += d;
8385
+ double = !double;
8386
+ }
8387
+ return digits.length > 0 && sum % 10 === 0;
8388
+ }
8389
+ const UUID_RE = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi;
8390
+ const CARD_RE = /(?<![\w-])(?:\d[ -]?){12,15}\d(?![\w-])/g;
8391
+ function findCardNumber(text) {
8392
+ const scrubbed = text.replace(UUID_RE, (m) => " ".repeat(m.length));
8393
+ CARD_RE.lastIndex = 0;
8394
+ for (let m = CARD_RE.exec(scrubbed); m !== null; m = CARD_RE.exec(scrubbed)) {
8395
+ const digits = m[0].replace(/[ -]/g, "");
8396
+ if (digits.length < 13 || digits.length > 16) continue;
8397
+ if (/^(\d)\1+$/.test(digits)) continue;
8398
+ if (luhnValid(digits)) return true;
8399
+ }
8400
+ return false;
8401
+ }
8402
+ function cnpValid(digits) {
8403
+ if (!/^[1-9]\d{12}$/.test(digits)) return false;
8404
+ const mm = Number(digits.slice(3, 5));
8405
+ const dd = Number(digits.slice(5, 7));
8406
+ const jj = Number(digits.slice(7, 9));
8407
+ if (mm < 1 || mm > 12 || dd < 1 || dd > 31) return false;
8408
+ if (!(jj >= 1 && jj <= 52 || jj === 70)) return false;
8409
+ const w = "279146358279";
8410
+ let sum = 0;
8411
+ for (let i = 0; i < 12; i++) sum += (digits.charCodeAt(i) - 48) * (w.charCodeAt(i) - 48);
8412
+ return (sum % 11 === 10 ? 1 : sum % 11) === digits.charCodeAt(12) - 48;
8413
+ }
8414
+ const CNP_RE = /\b[1-9]\d{12}\b/g;
8415
+ function findCnp(text) {
8416
+ CNP_RE.lastIndex = 0;
8417
+ for (let m = CNP_RE.exec(text); m !== null; m = CNP_RE.exec(text)) if (cnpValid(m[0])) return true;
8418
+ return false;
8419
+ }
8420
+ const EMAIL_RE = /\b([A-Za-z0-9._%+-]+)@([A-Za-z0-9.-]+\.[A-Za-z]{2,})\b/g;
8421
+ const EMAIL_SKIP_DOMAIN = /(^|\.)(example\.(com|net|org)|example|test|invalid|localhost|localdomain)$|(^|\.)github\.com$/i;
8422
+ const EMAIL_SKIP_LOCAL = /^(git|noreply|no-reply|donotreply|do-not-reply)$/i;
8423
+ const EMAIL_SKIP_TLD_AS_EXT = /\.(png|jpe?g|gif|svg|webp|ico|css|js|ts|json|md|txt|html?|xml|yml|yaml|py)$/i;
8424
+ function findPersonalEmail(text) {
8425
+ EMAIL_RE.lastIndex = 0;
8426
+ for (let m = EMAIL_RE.exec(text); m !== null; m = EMAIL_RE.exec(text)) {
8427
+ const local = m[1] ?? "";
8428
+ const domain = m[2] ?? "";
8429
+ if (EMAIL_SKIP_DOMAIN.test(domain)) continue;
8430
+ if (EMAIL_SKIP_LOCAL.test(local)) continue;
8431
+ if (EMAIL_SKIP_TLD_AS_EXT.test(domain)) continue;
8432
+ return true;
8433
+ }
8434
+ return false;
8435
+ }
8436
+ const CRED_RE = /(?:^|[^A-Za-z0-9])[A-Za-z0-9_-]*?(password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|auth[_-]?token|client[_-]?secret)\b\s*[:=]\s*(?:new\s+\w+\s*\(\s*)?(["'`])([^"'`\r\n]{8,})\2/gi;
8437
+ const CRED_PLACEHOLDER = /^(\$\{|\{\{|<|%[(sd]|\$[A-Z_]|xxx|\*{3,}|\.{3,}|change[_-]?me|replace[_-]?me|your[_-]|my[_-]?(actual[_-]?)?(password|secret|token|key)|placeholder|example|dummy|sample|test(value|ing)?$|redacted|hunter2$|password$|secret$|123456)/i;
8438
+ function findCredentialLiteral(text) {
8439
+ CRED_RE.lastIndex = 0;
8440
+ for (let m = CRED_RE.exec(text); m !== null; m = CRED_RE.exec(text)) {
8441
+ const value = m[3] ?? "";
8442
+ if (CRED_PLACEHOLDER.test(value)) continue;
8443
+ if (/^[a-z_][a-z0-9_.]*$/i.test(value) && !/\d/.test(value)) continue;
8444
+ return true;
8445
+ }
8446
+ return false;
8447
+ }
8247
8448
  const SECRET_PATTERNS = [
8248
8449
  {
8249
8450
  name: "cnp",
8250
- re: /\b[1-9]\d{12}\b/
8451
+ kind: "pii",
8452
+ test: findCnp
8251
8453
  },
8252
8454
  {
8253
8455
  name: "email",
8254
- re: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/
8456
+ kind: "pii",
8457
+ test: findPersonalEmail
8255
8458
  },
8256
8459
  {
8257
8460
  name: "phoneRo",
8461
+ kind: "pii",
8258
8462
  re: /\b(\+4|0)7\d{8}\b/
8259
8463
  },
8260
8464
  {
8261
8465
  name: "card",
8262
- re: /\b(?:\d[ -]?){13,16}\b/
8466
+ kind: "pii",
8467
+ test: findCardNumber
8263
8468
  },
8264
8469
  {
8265
8470
  name: "credentialAssignment",
8266
- re: /\b(password|secret|token|api[_-]?key)\b\s*[:=]\s*["']?[^\s"']{4,}/i
8471
+ kind: "secret",
8472
+ test: findCredentialLiteral
8267
8473
  },
8268
8474
  {
8269
8475
  name: "awsKey",
8476
+ kind: "secret",
8270
8477
  re: /AKIA[0-9A-Z]{16}/
8271
8478
  },
8272
8479
  {
8273
8480
  name: "githubToken",
8481
+ kind: "secret",
8274
8482
  re: /gh[pousr]_[A-Za-z0-9]{36,}/
8275
8483
  },
8276
8484
  {
8277
8485
  name: "privateKey",
8486
+ kind: "secret",
8278
8487
  re: /-----BEGIN [A-Z ]*PRIVATE KEY-----/
8279
8488
  },
8280
8489
  {
8281
8490
  name: "jwt",
8491
+ kind: "secret",
8282
8492
  re: /eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/
8283
8493
  },
8284
8494
  {
8285
8495
  name: "slackToken",
8496
+ kind: "secret",
8286
8497
  re: /xox[abpr]-/
8287
8498
  }
8288
8499
  ];
8289
8500
  SECRET_PATTERNS.map((p) => p.name);
8501
+ SECRET_PATTERNS.filter((p) => p.kind === "pii").map((p) => p.name);
8290
8502
  const SCAN_LIMIT = 4194304;
8291
8503
  const contentNoSecrets = definePredicate({
8292
8504
  id: "content.noSecrets",
8293
8505
  params: object({
8294
8506
  disable: array(string()).default([]),
8295
- allowPaths: array(string().min(1)).default([])
8507
+ allowPaths: array(string().min(1)).default([]),
8508
+ pii: boolean().default(false)
8296
8509
  }).strict(),
8297
8510
  requires: ["manifest", "content"],
8298
- async run(ctx, { disable, allowPaths }) {
8511
+ async run(ctx, { disable, allowPaths, pii }) {
8299
8512
  const skip = globMatcher(allowPaths, false);
8300
- const active = SECRET_PATTERNS.filter((p) => !disable.includes(p.name));
8513
+ const active = SECRET_PATTERNS.filter((p) => !disable.includes(p.name) && (pii || p.kind === "secret"));
8301
8514
  const out = [];
8302
8515
  const decoder = new TextDecoder("utf-8", { fatal: false });
8303
8516
  for (const a of ctx.manifest.artifacts) {
@@ -8306,7 +8519,7 @@ const contentNoSecrets = definePredicate({
8306
8519
  if (bytes === void 0) continue;
8307
8520
  const text = decoder.decode(bytes.subarray(0, SCAN_LIMIT));
8308
8521
  for (const p of active) {
8309
- if (!p.re.test(text)) continue;
8522
+ if (!(p.test !== void 0 ? p.test(text) : p.re?.test(text) ?? false)) continue;
8310
8523
  out.push(finding({
8311
8524
  id: `content.noSecrets.${p.name}`,
8312
8525
  predicate: "content.noSecrets",
@@ -8362,7 +8575,7 @@ object({
8362
8575
  command: string().min(1),
8363
8576
  args: array(string()).default([]),
8364
8577
  cwd: _enum(["root", "staging"]).default("root"),
8365
- timeoutMs: int().min(1).max(6e4).default(3e4),
8578
+ timeoutMs: int().min(1).max(9e5).default(3e4),
8366
8579
  env: record(string(), string()).optional(),
8367
8580
  stdin: _enum([
8368
8581
  "bundle",
@@ -8426,9 +8639,15 @@ object({ rules: array(object({
8426
8639
  when: string().min(1),
8427
8640
  expect: array(object({
8428
8641
  name: string().min(1),
8429
- match: string().min(1)
8642
+ match: string().min(1),
8643
+ mustChange: boolean().default(false)
8430
8644
  }).strict()).min(1)
8431
8645
  }).strict()).min(1) }).strict();
8646
+ object({
8647
+ minSignatures: int().min(1).max(16).default(1),
8648
+ antiRollback: boolean().default(false),
8649
+ trustFile: string().min(1).default(".axiom/trust/keys.json")
8650
+ }).strict();
8432
8651
  Math.max(1, Math.min(4, cpus().length));
8433
8652
  const realpathNative = promisify(realpath.native);
8434
8653
  const IS_WIN32 = process.platform === "win32";
@@ -8440,6 +8659,7 @@ const GateProfileSchema = object({
8440
8659
  deny: array(string().min(1)).default([]),
8441
8660
  allow: array(string().min(1)).optional(),
8442
8661
  noSecrets: boolean().default(true),
8662
+ pii: boolean().default(false),
8443
8663
  maxBytes: int().positive().optional()
8444
8664
  }).strict();
8445
8665
  const DEFAULT_GATE_PROFILE = {
@@ -8452,7 +8672,8 @@ const DEFAULT_GATE_PROFILE = {
8452
8672
  ".env.*",
8453
8673
  "**/node_modules/**"
8454
8674
  ],
8455
- noSecrets: true
8675
+ noSecrets: true,
8676
+ pii: false
8456
8677
  };
8457
8678
  async function loadGateProfile(opts) {
8458
8679
  const candidates = [];
@@ -8595,6 +8816,262 @@ function isWriteTool(toolName) {
8595
8816
  if (n.includes("apply_patch") || n.includes("multi_replace")) return true;
8596
8817
  return !NON_WRITE_HINTS.some((h) => n.includes(h));
8597
8818
  }
8819
+ const SHELL_TOOL_HINTS = [
8820
+ "bash",
8821
+ "shell",
8822
+ "run_in_terminal",
8823
+ "terminal",
8824
+ "execute_command",
8825
+ "exec_command",
8826
+ "run_command",
8827
+ "powershell",
8828
+ "cmd"
8829
+ ];
8830
+ const SHELL_COMMAND_KEYS = [
8831
+ "command",
8832
+ "cmd",
8833
+ "script",
8834
+ "commandLine",
8835
+ "command_line"
8836
+ ];
8837
+ function isShellTool(toolName) {
8838
+ const n = toolName.toLowerCase();
8839
+ return SHELL_TOOL_HINTS.some((h) => n.includes(h));
8840
+ }
8841
+ function classifyTool(toolName) {
8842
+ if (isWriteTool(toolName)) return "write";
8843
+ if (isShellTool(toolName)) return "shell";
8844
+ return "other";
8845
+ }
8846
+ const REDIRECT_RE = /(?:^|[\s;&|])(?:\d?>{1,2}|&>)\s*("([^"]+)"|'([^']+)'|([^\s;&|<>]+))/g;
8847
+ const SHELL_SPLIT_RE = /(?:^|[;&|]+|\|\||&&)\s*/;
8848
+ const WRITE_CMDS = {
8849
+ tee: {
8850
+ skipFlags: true,
8851
+ operands: "all"
8852
+ },
8853
+ rm: {
8854
+ skipFlags: true,
8855
+ operands: "all"
8856
+ },
8857
+ rmdir: {
8858
+ skipFlags: true,
8859
+ operands: "all"
8860
+ },
8861
+ mv: {
8862
+ skipFlags: true,
8863
+ operands: "last"
8864
+ },
8865
+ cp: {
8866
+ skipFlags: true,
8867
+ operands: "last"
8868
+ },
8869
+ touch: {
8870
+ skipFlags: true,
8871
+ operands: "all"
8872
+ },
8873
+ truncate: {
8874
+ skipFlags: true,
8875
+ operands: "all"
8876
+ },
8877
+ mkdir: {
8878
+ skipFlags: true,
8879
+ operands: "all"
8880
+ },
8881
+ ln: {
8882
+ skipFlags: true,
8883
+ operands: "last"
8884
+ },
8885
+ chmod: {
8886
+ skipFlags: true,
8887
+ operands: "all"
8888
+ },
8889
+ unlink: {
8890
+ skipFlags: true,
8891
+ operands: "all"
8892
+ },
8893
+ install: {
8894
+ skipFlags: true,
8895
+ operands: "last"
8896
+ },
8897
+ dd: {
8898
+ skipFlags: false,
8899
+ operands: "all"
8900
+ },
8901
+ sed: {
8902
+ skipFlags: true,
8903
+ operands: "afterI"
8904
+ },
8905
+ "remove-item": {
8906
+ skipFlags: true,
8907
+ operands: "all"
8908
+ },
8909
+ "set-content": {
8910
+ skipFlags: true,
8911
+ operands: "all"
8912
+ },
8913
+ "add-content": {
8914
+ skipFlags: true,
8915
+ operands: "all"
8916
+ },
8917
+ "out-file": {
8918
+ skipFlags: true,
8919
+ operands: "all"
8920
+ },
8921
+ "move-item": {
8922
+ skipFlags: true,
8923
+ operands: "all"
8924
+ },
8925
+ "copy-item": {
8926
+ skipFlags: true,
8927
+ operands: "all"
8928
+ },
8929
+ "new-item": {
8930
+ skipFlags: true,
8931
+ operands: "all"
8932
+ },
8933
+ del: {
8934
+ skipFlags: true,
8935
+ operands: "all"
8936
+ },
8937
+ erase: {
8938
+ skipFlags: true,
8939
+ operands: "all"
8940
+ },
8941
+ move: {
8942
+ skipFlags: true,
8943
+ operands: "all"
8944
+ },
8945
+ copy: {
8946
+ skipFlags: true,
8947
+ operands: "all"
8948
+ }
8949
+ };
8950
+ const GIT_WRITE_SUBCMDS = /* @__PURE__ */ new Set([
8951
+ "checkout",
8952
+ "reset",
8953
+ "clean",
8954
+ "restore",
8955
+ "rm",
8956
+ "mv",
8957
+ "stash"
8958
+ ]);
8959
+ function shellWords(segment) {
8960
+ const out = [];
8961
+ const re = /"([^"]*)"|'([^']*)'|(\S+)/g;
8962
+ for (let m = re.exec(segment); m !== null; m = re.exec(segment)) out.push(m[1] ?? m[2] ?? m[3] ?? "");
8963
+ return out;
8964
+ }
8965
+ function looksLikePath(word) {
8966
+ if (word === "" || word === "-" || word === "--") return false;
8967
+ if (word.startsWith("-") && !word.startsWith("./") && !word.startsWith("../")) return false;
8968
+ if (/^\$\(|^`|^\$[A-Za-z_{]/.test(word)) return false;
8969
+ return true;
8970
+ }
8971
+ function extractShellWriteTargets(command) {
8972
+ const out = [];
8973
+ const seen = /* @__PURE__ */ new Set();
8974
+ const push = (rawPath, op) => {
8975
+ if (!looksLikePath(rawPath) || seen.has(rawPath)) return;
8976
+ seen.add(rawPath);
8977
+ out.push({
8978
+ rawPath,
8979
+ op,
8980
+ content: void 0
8981
+ });
8982
+ };
8983
+ for (let m = REDIRECT_RE.exec(command); m !== null; m = REDIRECT_RE.exec(command)) {
8984
+ const target = m[2] ?? m[3] ?? m[4] ?? "";
8985
+ if (target !== "" && !/^&\d$/.test(target) && !/^\/dev\//.test(target)) push(target, "write");
8986
+ }
8987
+ REDIRECT_RE.lastIndex = 0;
8988
+ for (const seg of command.split(SHELL_SPLIT_RE)) {
8989
+ const words = shellWords(seg);
8990
+ let i = 0;
8991
+ while (i < words.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(words[i] ?? "")) i++;
8992
+ while (i < words.length && [
8993
+ "sudo",
8994
+ "exec",
8995
+ "command",
8996
+ "nohup",
8997
+ "time"
8998
+ ].includes(words[i] ?? "")) i++;
8999
+ const cmd = (words[i] ?? "").toLowerCase().replace(/^.*[\\/]/, "");
9000
+ const rest = words.slice(i + 1);
9001
+ if (cmd === "git") {
9002
+ const sub = rest.find((w) => !w.startsWith("-"));
9003
+ if (sub !== void 0 && GIT_WRITE_SUBCMDS.has(sub)) {
9004
+ const afterSub = rest.slice(rest.indexOf(sub) + 1);
9005
+ const dd = afterSub.indexOf("--");
9006
+ const operands = dd === -1 ? afterSub.filter((w) => !w.startsWith("-")) : afterSub.slice(dd + 1);
9007
+ if (sub === "clean" || sub === "reset" && afterSub.includes("--hard")) push(".", "delete");
9008
+ for (const p of operands) push(p, sub === "rm" || sub === "clean" ? "delete" : "write");
9009
+ }
9010
+ continue;
9011
+ }
9012
+ const rule = WRITE_CMDS[cmd];
9013
+ if (rule === void 0) continue;
9014
+ if (cmd === "sed") {
9015
+ if (!rest.some((w) => /^-[a-zA-Z]*i/.test(w) || w === "--in-place" || w.startsWith("--in-place="))) continue;
9016
+ const hasScriptFlag = rest.some((w) => /^-[a-zA-Z]*[ef]/.test(w) || /^--(expression|file)/.test(w));
9017
+ const nonFlags = [];
9018
+ for (let k = 0; k < rest.length; k++) {
9019
+ const w = rest[k] ?? "";
9020
+ if (w.startsWith("-")) {
9021
+ if (/^-[a-zA-Z]*[ef]$/.test(w) || w === "--expression" || w === "--file") k++;
9022
+ continue;
9023
+ }
9024
+ nonFlags.push(w);
9025
+ }
9026
+ const files = hasScriptFlag ? nonFlags : nonFlags.slice(1);
9027
+ for (const w of files) push(w, "write");
9028
+ continue;
9029
+ }
9030
+ if (cmd === "dd") {
9031
+ for (const w of rest) if (w.startsWith("of=")) push(w.slice(3), "write");
9032
+ continue;
9033
+ }
9034
+ let operands;
9035
+ if (cmd.includes("-")) {
9036
+ operands = [];
9037
+ const PATH_PARAMS = [
9038
+ "path",
9039
+ "literalpath",
9040
+ "filepath",
9041
+ "destination",
9042
+ "newname",
9043
+ "target"
9044
+ ];
9045
+ const VALUE_PARAMS = /* @__PURE__ */ new Set([
9046
+ ...PATH_PARAMS,
9047
+ "value",
9048
+ "encoding",
9049
+ "filter",
9050
+ "include",
9051
+ "exclude",
9052
+ "itemtype",
9053
+ "name"
9054
+ ]);
9055
+ for (let k = 0; k < rest.length; k++) {
9056
+ const w = rest[k] ?? "";
9057
+ if (w.startsWith("-")) {
9058
+ const name = w.slice(1).toLowerCase();
9059
+ const val = rest[k + 1];
9060
+ if (PATH_PARAMS.includes(name) && val !== void 0) operands.push(val);
9061
+ if (VALUE_PARAMS.has(name) && val !== void 0 && !val.startsWith("-")) k++;
9062
+ continue;
9063
+ }
9064
+ operands.push(w);
9065
+ }
9066
+ } else operands = rule.skipFlags ? rest.filter((w) => !w.startsWith("-") || w === "-") : rest;
9067
+ const op = cmd === "rm" || cmd === "rmdir" || cmd === "unlink" || cmd === "del" || cmd === "erase" || cmd === "remove-item" ? "delete" : "write";
9068
+ if (rule.operands === "last") {
9069
+ const last = operands[operands.length - 1];
9070
+ if (last !== void 0) push(last, op);
9071
+ } else for (const w of operands) push(w, op);
9072
+ }
9073
+ return out;
9074
+ }
8598
9075
  const PATCH_HEADER = /^\*\*\* (Update|Add|Delete) File: (.+?)\s*$/;
8599
9076
  const PATCH_MOVE = /^\*\*\* Move to: (.+?)\s*$/;
8600
9077
  function parseApplyPatch(patch) {
@@ -8639,7 +9116,33 @@ function joinContent(parts) {
8639
9116
  return present.length === 0 ? void 0 : present.join("\n");
8640
9117
  }
8641
9118
  function extractTargets(p) {
8642
- if (!isWriteTool(p.toolName)) return [];
9119
+ const cls = classifyTool(p.toolName);
9120
+ if (cls === "other") return {
9121
+ cls,
9122
+ targets: [],
9123
+ undetermined: false
9124
+ };
9125
+ if (cls === "shell") {
9126
+ const command = firstString(p.input, SHELL_COMMAND_KEYS);
9127
+ if (command === void 0) return {
9128
+ cls,
9129
+ targets: [],
9130
+ undetermined: false
9131
+ };
9132
+ return {
9133
+ cls,
9134
+ targets: extractShellWriteTargets(command),
9135
+ undetermined: false
9136
+ };
9137
+ }
9138
+ const targets = extractWriteTargets(p);
9139
+ return {
9140
+ cls,
9141
+ targets,
9142
+ undetermined: targets.length === 0
9143
+ };
9144
+ }
9145
+ function extractWriteTargets(p) {
8643
9146
  const n = p.toolName.toLowerCase();
8644
9147
  const input = p.input;
8645
9148
  if (n.includes("apply_patch")) {
@@ -8697,9 +9200,9 @@ const PATH_CODES = /* @__PURE__ */ new Set([
8697
9200
  "ERR_PATH_RESERVED_NAME",
8698
9201
  "ERR_PATH_INVALID_CHAR"
8699
9202
  ]);
8700
- async function resolveGateTarget(rootReal, target) {
9203
+ async function resolveGateTarget(rootReal, target, cwdReal = rootReal) {
8701
9204
  const supplied = target.rawPath.replace(/\\/g, "/");
8702
- let abs = path.isAbsolute(supplied) ? path.resolve(supplied) : path.resolve(rootReal, supplied);
9205
+ let abs = path.isAbsolute(supplied) ? path.resolve(supplied) : path.resolve(cwdReal, supplied);
8703
9206
  if (path.isAbsolute(supplied)) abs = await canonicalizeExisting(abs);
8704
9207
  const rel = path.relative(rootReal, abs);
8705
9208
  if (rel === "" || rel === "." || rel.startsWith("..") || path.isAbsolute(rel)) throw new GateDeny("ERR_CONTAINMENT", "target escapes root", supplied);
@@ -8789,7 +9292,8 @@ async function runGatePredicates(targets, profile) {
8789
9292
  const hasContent = targets.some((t) => t.target.content !== void 0);
8790
9293
  if (hasContent && profile.noSecrets) findings.push(...await contentNoSecrets.run(ctx, {
8791
9294
  disable: [],
8792
- allowPaths: []
9295
+ allowPaths: [],
9296
+ pii: profile.pii
8793
9297
  }));
8794
9298
  if (hasContent && profile.maxBytes !== void 0) findings.push(...await contentMaxBytes.run(ctx, { max: profile.maxBytes }));
8795
9299
  const f = findings[0];
@@ -8808,29 +9312,84 @@ const RANK = {
8808
9312
  info: 2,
8809
9313
  debug: 3
8810
9314
  };
8811
- function denyResult(d, stderr, t0) {
9315
+ function denyDocument(d, reason, extra) {
9316
+ return {
9317
+ hookSpecificOutput: {
9318
+ hookEventName: "PreToolUse",
9319
+ permissionDecision: "deny",
9320
+ permissionDecisionReason: reason
9321
+ },
9322
+ permissionDecision: "deny",
9323
+ permissionDecisionReason: reason,
9324
+ axiom: {
9325
+ verdict: "deny",
9326
+ code: d.code,
9327
+ ...d.relPath === void 0 ? {} : { path: d.relPath },
9328
+ ...extra.toolClass === void 0 ? {} : { toolClass: extra.toolClass },
9329
+ standard: "owasp-acs/0.1"
9330
+ }
9331
+ };
9332
+ }
9333
+ function denyResult(d, stderr, t0, extra = {}) {
8812
9334
  const where = d.relPath === void 0 ? "" : ` (${d.relPath})`;
8813
9335
  const reason = `AXIOM GATE DENY ${d.code}: ${d.message}${where}`;
8814
9336
  stderr.push(reason);
8815
9337
  return {
8816
9338
  decision: "deny",
9339
+ verdict: "deny",
8817
9340
  exitCode: 2,
8818
9341
  code: d.code,
8819
9342
  reason: d.message,
8820
9343
  ...d.relPath === void 0 ? {} : { path: d.relPath },
8821
- stdout: JSON.stringify({ hookSpecificOutput: {
8822
- hookEventName: "PreToolUse",
8823
- permissionDecision: "deny",
8824
- permissionDecisionReason: reason
8825
- } }),
9344
+ stdout: JSON.stringify(denyDocument(d, reason, extra)),
8826
9345
  stderr,
8827
- durationMs: performance.now() - t0
8828
- };
9346
+ durationMs: performance.now() - t0,
9347
+ ...extra.toolClass === void 0 ? {} : { toolClass: extra.toolClass },
9348
+ ...extra.root === void 0 ? {} : { root: extra.root }
9349
+ };
9350
+ }
9351
+ const AXIOM_REPO_MARKERS = [
9352
+ "journal",
9353
+ "cas",
9354
+ "applied",
9355
+ "profiles",
9356
+ "trust",
9357
+ "lock",
9358
+ "backup"
9359
+ ];
9360
+ async function discoverRoot(start, home = homedir()) {
9361
+ const homeReal = path.resolve(home);
9362
+ const isHomeOrAbove = (p) => {
9363
+ const a = IS_WIN32 ? p.toLowerCase() : p;
9364
+ const h = IS_WIN32 ? homeReal.toLowerCase() : homeReal;
9365
+ return a === h || h.startsWith(a.endsWith(path.sep) ? a : a + path.sep);
9366
+ };
9367
+ let cur = start;
9368
+ for (let depth = 0; depth < 64; depth++) {
9369
+ if (isHomeOrAbove(cur)) break;
9370
+ try {
9371
+ const st = await lstat(path.join(cur, ".git"));
9372
+ if (st.isDirectory() || st.isFile()) return cur;
9373
+ } catch {}
9374
+ try {
9375
+ const ax = path.join(cur, ".axiom");
9376
+ if ((await lstat(ax)).isDirectory()) for (const m of AXIOM_REPO_MARKERS) try {
9377
+ await lstat(path.join(ax, m));
9378
+ return cur;
9379
+ } catch {}
9380
+ } catch {}
9381
+ const parent = path.dirname(cur);
9382
+ if (parent === cur) break;
9383
+ cur = parent;
9384
+ }
9385
+ return start;
8829
9386
  }
8830
9387
  async function runGate(payloadText, opts = {}) {
8831
9388
  const t0 = performance.now();
8832
9389
  const level = opts.logLevel ?? "warn";
8833
9390
  const stderr = [];
9391
+ let toolClass;
9392
+ let rootUsed;
8834
9393
  const log = (lvl, msg) => {
8835
9394
  if (RANK[lvl] <= RANK[level]) stderr.push(`AXIOM GATE ${lvl.toUpperCase()}: ${msg}`);
8836
9395
  };
@@ -8838,15 +9397,24 @@ async function runGate(payloadText, opts = {}) {
8838
9397
  log("debug", `allow: ${why}`);
8839
9398
  return {
8840
9399
  decision: "allow",
9400
+ verdict: "allow",
8841
9401
  exitCode: 0,
8842
9402
  stderr,
8843
- durationMs: performance.now() - t0
9403
+ durationMs: performance.now() - t0,
9404
+ ...toolClass === void 0 ? {} : { toolClass },
9405
+ ...rootUsed === void 0 ? {} : { root: rootUsed }
8844
9406
  };
8845
9407
  };
9408
+ const deny = (d) => denyResult(d, stderr, t0, {
9409
+ ...toolClass === void 0 ? {} : { toolClass },
9410
+ ...rootUsed === void 0 ? {} : { root: rootUsed }
9411
+ });
8846
9412
  const internal = (why) => {
8847
- if (opts.strict === true) return denyResult(new GateDeny("ERR_INTERNAL", `${why} (--strict)`), stderr, t0);
8848
- log("warn", `${why} — failing open`);
8849
- return allow("internal error");
9413
+ if (opts.failOpen === true) {
9414
+ log("warn", `${why} — failing open (--fail-open)`);
9415
+ return allow("internal error");
9416
+ }
9417
+ return deny(new GateDeny("ERR_INTERNAL", `${why} (fail-closed; pass --fail-open to allow)`));
8850
9418
  };
8851
9419
  if (payloadText === void 0) return internal("no payload on stdin (timeout or empty)");
8852
9420
  let raw;
@@ -8856,26 +9424,61 @@ async function runGate(payloadText, opts = {}) {
8856
9424
  return internal(`payload is not JSON: ${err instanceof Error ? err.message : String(err)}`);
8857
9425
  }
8858
9426
  const payload = normalizePayload(raw);
8859
- if (payload === void 0) return allow("payload carries no tool name");
8860
- const targets = extractTargets(payload);
8861
- if (targets.length === 0) return allow(`tool ${payload.toolName} has no write target`);
9427
+ if (payload === void 0) {
9428
+ log("info", "payload carries no tool name");
9429
+ return allow("payload carries no tool name");
9430
+ }
9431
+ const extracted = extractTargets(payload);
9432
+ toolClass = extracted.cls;
9433
+ if (extracted.cls === "other") return allow(`tool ${payload.toolName} is not a write/shell tool`);
9434
+ if (extracted.cls === "shell" && opts.noShellScan === true) return allow(`tool ${payload.toolName} is a shell tool and --no-shell-scan is set`);
9435
+ if (extracted.undetermined) {
9436
+ if (opts.failOpen === true) {
9437
+ log("warn", `write tool ${payload.toolName} has no recognised path key — allowing (--fail-open)`);
9438
+ return allow("undetermined write target");
9439
+ }
9440
+ return deny(new GateDeny("ERR_UNSUPPORTED_OP", `write tool "${payload.toolName}" carries no recognised path key (${PATH_KEYS.join(", ")}); add it to the gate or pass --fail-open`));
9441
+ }
9442
+ const targets = extracted.targets;
9443
+ if (targets.length === 0) return allow(`tool ${payload.toolName} writes nothing recognisable`);
8862
9444
  try {
8863
9445
  const rootArg = payload.cwd ?? opts.root ?? (opts.cwdFallback ?? (() => process.cwd()))();
8864
9446
  let rootReal = await realpathNative(path.resolve(rootArg));
8865
9447
  if (IS_WIN32 && rootReal.startsWith("\\\\?\\")) rootReal = rootReal.slice(4);
9448
+ const cwdReal = rootReal;
9449
+ if (opts.noRootDiscovery !== true) {
9450
+ const discovered = await discoverRoot(rootReal, opts.home);
9451
+ if (discovered !== rootReal) {
9452
+ log("debug", `root discovered ${rootReal} → ${discovered}`);
9453
+ rootReal = discovered;
9454
+ }
9455
+ }
9456
+ rootUsed = rootReal;
8866
9457
  const { profile, source } = await loadGateProfile({
8867
9458
  root: rootReal,
8868
9459
  ...opts.profilePath === void 0 ? {} : { profilePath: opts.profilePath },
8869
9460
  ...opts.home === void 0 ? {} : { home: opts.home }
8870
9461
  });
8871
- log("debug", `root=${rootReal} profile=${source} targets=${targets.length}`);
9462
+ log("debug", `root=${rootReal} profile=${source} class=${extracted.cls} targets=${targets.length}`);
8872
9463
  const resolved = [];
8873
- for (const t of targets) resolved.push(await resolveGateTarget(rootReal, t));
8874
- const deny = await runGatePredicates(resolved, profile);
8875
- if (deny !== void 0) return denyResult(deny, stderr, t0);
8876
- return allow(`${resolved.length} target(s) passed`);
9464
+ for (const t of targets) try {
9465
+ resolved.push(await resolveGateTarget(rootReal, t, cwdReal));
9466
+ } catch (err) {
9467
+ if (extracted.cls === "shell" && err instanceof GateDeny && err.code !== "ERR_CONTAINMENT") {
9468
+ log("debug", `shell operand ${JSON.stringify(t.rawPath)} skipped: ${err.code}`);
9469
+ continue;
9470
+ }
9471
+ if (extracted.cls === "shell" && err instanceof GateDeny && t.rawPath === ".") {
9472
+ if (profile.deny.length > 0) throw new GateDeny("path.deny", "destructive git command on the whole tree", ".");
9473
+ continue;
9474
+ }
9475
+ throw err;
9476
+ }
9477
+ const verdict = await runGatePredicates(resolved, profile);
9478
+ if (verdict !== void 0) return deny(verdict);
9479
+ return allow(`${resolved.length} target(s) passed (${extracted.cls})`);
8877
9480
  } catch (err) {
8878
- if (err instanceof GateDeny) return denyResult(err, stderr, t0);
9481
+ if (err instanceof GateDeny) return deny(err);
8879
9482
  return internal(err instanceof Error ? err.message : String(err));
8880
9483
  }
8881
9484
  }
@@ -8925,7 +9528,7 @@ function readStdin(stream, { timeoutMs = GATE_STDIN_TIMEOUT_MS, maxBytes = GATE_
8925
9528
  stream.resume();
8926
9529
  });
8927
9530
  }
8928
- const GATE_USAGE = "usage: axiom gate --stdin [--root <dir>] [--profile <file>] [--strict] [--log-level error|warn|info|debug]";
9531
+ const GATE_USAGE = "usage: axiom gate --stdin [--root <dir>] [--profile <file>] [--fail-open] [--no-shell-scan] [--no-root-discovery] [--log-level error|warn|info|debug]";
8929
9532
  async function gateMain(argv, io = {}) {
8930
9533
  const t0 = performance.now();
8931
9534
  let values;
@@ -8937,6 +9540,9 @@ async function gateMain(argv, io = {}) {
8937
9540
  root: { type: "string" },
8938
9541
  profile: { type: "string" },
8939
9542
  strict: { type: "boolean" },
9543
+ "fail-open": { type: "boolean" },
9544
+ "no-shell-scan": { type: "boolean" },
9545
+ "no-root-discovery": { type: "boolean" },
8940
9546
  "log-level": { type: "string" }
8941
9547
  },
8942
9548
  strict: true,
@@ -8945,6 +9551,7 @@ async function gateMain(argv, io = {}) {
8945
9551
  } catch (err) {
8946
9552
  return {
8947
9553
  decision: "deny",
9554
+ verdict: "deny",
8948
9555
  exitCode: 2,
8949
9556
  reason: err instanceof Error ? err.message : String(err),
8950
9557
  stderr: [`AXIOM GATE ERROR: ${err instanceof Error ? err.message : String(err)}`, GATE_USAGE],
@@ -8953,6 +9560,7 @@ async function gateMain(argv, io = {}) {
8953
9560
  }
8954
9561
  if (values.stdin !== true) return {
8955
9562
  decision: "deny",
9563
+ verdict: "deny",
8956
9564
  exitCode: 2,
8957
9565
  reason: "--stdin is required",
8958
9566
  stderr: ["AXIOM GATE ERROR: --stdin is required", GATE_USAGE],
@@ -8965,6 +9573,9 @@ async function gateMain(argv, io = {}) {
8965
9573
  if (values.root !== void 0) opts.root = values.root;
8966
9574
  if (values.profile !== void 0) opts.profilePath = values.profile;
8967
9575
  if (values.strict === true) opts.strict = true;
9576
+ if (values["fail-open"] === true) opts.failOpen = true;
9577
+ if (values["no-shell-scan"] === true) opts.noShellScan = true;
9578
+ if (values["no-root-discovery"] === true) opts.noRootDiscovery = true;
8968
9579
  if (io.cwdFallback !== void 0) opts.cwdFallback = io.cwdFallback;
8969
9580
  if (io.home !== void 0) opts.home = io.home;
8970
9581
  if (read.tooLarge) {