@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/README.md +104 -21
- package/dist/axm-lazy-LAnrfjUO.js +6284 -0
- package/dist/axm-lazy-Xin1nh7Q.js +6284 -0
- package/dist/axm-lazy.js +196 -7
- package/dist/cli-main.js +3661 -14919
- package/dist/dist-Bt9Wvd_P.js +6379 -0
- package/dist/dist-D4M2Lyji.js +6378 -0
- package/dist/gate-lazy.js +654 -43
- package/dist/gc-lazy.js +6885 -0
- package/dist/http-lazy.js +23527 -0
- package/dist/index.d.ts +141 -5
- package/dist/index.js +974 -50
- package/dist/lib-CqHwM4m_.js +3884 -0
- package/dist/mcp-lazy.js +23050 -0
- package/dist/migrate-lazy.js +6513 -0
- package/dist/verify-tree-lazy.js +6893 -0
- package/package.json +15 -8
- package/spec/codai-tools.json +348 -2
- package/spec/tools.json +1721 -72
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("
|
|
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)(
|
|
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
|
-
|
|
8451
|
+
kind: "pii",
|
|
8452
|
+
test: findCnp
|
|
8251
8453
|
},
|
|
8252
8454
|
{
|
|
8253
8455
|
name: "email",
|
|
8254
|
-
|
|
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
|
-
|
|
8466
|
+
kind: "pii",
|
|
8467
|
+
test: findCardNumber
|
|
8263
8468
|
},
|
|
8264
8469
|
{
|
|
8265
8470
|
name: "credentialAssignment",
|
|
8266
|
-
|
|
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.
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
|
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(
|
|
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.
|
|
8848
|
-
|
|
8849
|
-
|
|
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)
|
|
8860
|
-
|
|
8861
|
-
|
|
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)
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
|
|
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
|
|
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>] [--
|
|
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) {
|