@chain305/x-security 0.2.1 → 0.2.3
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 +20 -2
- package/bin/xsecurity.mjs +31 -27
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
|
-
#
|
|
1
|
+
# x-security CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Your API gateway and WAF already ship most of the security you need — auth, rate
|
|
4
|
+
limits, request validation, ownership/BOLA rules. Most teams run them on
|
|
5
|
+
defaults. `x-security` lets you write that policy **once**, as an extension of
|
|
6
|
+
the OpenAPI spec you already have, and compile it to whichever gateway you run.
|
|
7
|
+
|
|
8
|
+
- **No new DSL** — policy lives in your OpenAPI file, next to the route it protects.
|
|
9
|
+
- **No vendor lock-in** — one spec compiles to Kong, Coraza, BunkerWeb, OpenAppSec, Envoy, or a firewall bundle. Switch gateways without rewriting a line of policy.
|
|
10
|
+
- **No config drift** — the spec is the source of truth; it versions and diffs like code, and `validate` catches drift against a running gateway.
|
|
11
|
+
|
|
12
|
+
Deterministic. No LLM calls, no API keys.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
npm i -g @chain305/x-security # installs the `xsecurity` command
|
|
18
|
+
xsecurity --help
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or run it without installing:
|
|
4
22
|
|
|
5
23
|
```
|
|
6
24
|
npx @chain305/x-security --help
|
package/bin/xsecurity.mjs
CHANGED
|
@@ -25252,7 +25252,7 @@ var init_x_security_schema = __esm({
|
|
|
25252
25252
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
25253
25253
|
$id: "https://usewaf.com/schemas/x-security/v0.8.json",
|
|
25254
25254
|
title: "x-security",
|
|
25255
|
-
description: "Vendor-neutral OpenAPI extension declaring per-endpoint API security policy.
|
|
25255
|
+
description: "Vendor-neutral OpenAPI extension declaring per-endpoint API security policy. x-security compiles this into Kong, Coraza, BunkerWeb, OpenAppSec, Cloudflare, AWS API Gateway, and host-firewall configurations. v0.8 (deferred-residuals) adds: graphql.operations[] per-operation authz + cost (capKey graphql.operations.authz, API1/API5, OVERRIDE-ONLY everywhere \u2014 requires an operator-supplied GraphQL-aware processor; the coarse block limits get the distinct capKey graphql.staticLimits, API4); request.serializeBy + request.concurrencyLimit live-concurrency serialization (API6, PARTIAL at best \u2014 edge serialization only, NOT in-handler transaction atomicity); and request.dataAtRest ADVISORY-ONLY at-rest posture declaration (x-security-native SSEC-STORAGE, NOT gateway-enforced \u2014 compiles to nothing, drives an out-of-band scan finding). The unreachable residuals token-entropy and session-rotation get ZERO new fields (detect-and-flag / documented gateway non-goal). v0.7 (edge-enforceable-residuals) adds: injectionGuard sinks 'deserialization' (SSEC-INJECTION) and 'ai-prompt' (x-security-native SSEC-PROMPT); top-level audit logging policy (logging, x-security-native SSEC-AUDIT); response.forbidArrayRoot JSON-hijacking defense (API3); request.idempotencyKey replay/double-submit defense (API6); and capability wiring for the already-declared authentication.passwordPolicy / authentication.accountLockout enforcement (API2). v0.6 added: per-arg injection hardening (W19 request.schema.<f>.injectionGuard, attributed to the x-security-native SSEC-INJECTION category). v0.5 added: identity-flexibility (S-10 RuleRef namespaces: principal/session/header), outbound trust (S-11 outboundCalls), auth depth (S-12 mfa/lockout/passwordPolicy/tokenSources), error scrubbing (S-13), composite-ID semantics (S-14 identifier combinator), open-redirect (S-15 redirectAllowedDomains), session fixation (S-16 sessionRotateOnAuth), nonce replay (S-17 nonceCacheTtl), TLS floor (S-18 tls).",
|
|
25256
25256
|
type: "object",
|
|
25257
25257
|
additionalProperties: false,
|
|
25258
25258
|
properties: {
|
|
@@ -25334,7 +25334,7 @@ var init_x_security_schema = __esm({
|
|
|
25334
25334
|
pattern: "^API([1-9]|10):2023$"
|
|
25335
25335
|
},
|
|
25336
25336
|
SsecId: {
|
|
25337
|
-
description: "
|
|
25337
|
+
description: "x-security-native security category id for classes OWASP-API-2023 does not enumerate (Injection, LLM prompt-injection, audit logging, at-rest storage posture). Kept as a parallel pattern so the pure OWASP cells (^API([1-9]|10):2023$) cannot be overloaded or corrupted. v0.7 adds PROMPT (LLM prompt-injection) and AUDIT (audit logging) alongside INJECTION. v0.8 (deferred-residuals) adds STORAGE \u2014 an ADVISORY-ONLY at-rest posture class (encrypted/hashed/tokenized declaration). STORAGE is structurally unreachable by the gateway (it never sees the DB write); it NEVER compiles to enforcement and its capability is hard-pinned override-only/unsupported on every target. It exists only to document declared posture and drive an out-of-band scan finding.",
|
|
25338
25338
|
type: "string",
|
|
25339
25339
|
pattern: "^SSEC-(INJECTION|PROMPT|AUDIT|STORAGE)$"
|
|
25340
25340
|
},
|
|
@@ -25797,7 +25797,7 @@ var init_x_security_schema = __esm({
|
|
|
25797
25797
|
type: "boolean"
|
|
25798
25798
|
},
|
|
25799
25799
|
injectionGuard: {
|
|
25800
|
-
description: "v0.6 W19 (v0.7 adds 'deserialization' + 'ai-prompt'): opt-in per-arg injection hardening. Declares which injection sink(s) this body/query field flows into so the ModSecurity-family targets (coraza, bunkerweb) emit a native operator on ARGS:<field>|ARGS:json.<field> (covers both query/form args and the JSON body): 'sql' \u2192 @detectSQLi, 'nosql' \u2192 $where/$ne/$gt/$regex operator-token denylist, 'os-command' \u2192 shell-metacharacter @rx denylist ([;&|`$(){}<>] / newline), 'xpath' \u2192 XPath-metacharacter denylist, 'ldap' \u2192 LDAP-special-char denylist, 'code-eval' \u2192 eval/exec-marker denylist, 'xss' \u2192 @detectXSS (native cross-site-scripting operator), 'deserialization' \u2192 unsafe-deserialization preamble denylist (node-serialize _$$ND_FUNC$$_, Java rO0 base64, PHP O:<n>:, python pickle \\x80 opcode framing), 'ai-prompt' \u2192 LLM prompt-injection heuristic denylist (jailbreak / system-prompt-leak / role-override markers). Per-arg and explicit \u2014 replaces the implicit string-type heuristic. 'sql'/'nosql'/'os-command'/'xpath'/'ldap'/'code-eval'/'xss'/'deserialization' are attributed to SSEC-INJECTION; 'ai-prompt' is attributed to the distinct
|
|
25800
|
+
description: "v0.6 W19 (v0.7 adds 'deserialization' + 'ai-prompt'): opt-in per-arg injection hardening. Declares which injection sink(s) this body/query field flows into so the ModSecurity-family targets (coraza, bunkerweb) emit a native operator on ARGS:<field>|ARGS:json.<field> (covers both query/form args and the JSON body): 'sql' \u2192 @detectSQLi, 'nosql' \u2192 $where/$ne/$gt/$regex operator-token denylist, 'os-command' \u2192 shell-metacharacter @rx denylist ([;&|`$(){}<>] / newline), 'xpath' \u2192 XPath-metacharacter denylist, 'ldap' \u2192 LDAP-special-char denylist, 'code-eval' \u2192 eval/exec-marker denylist, 'xss' \u2192 @detectXSS (native cross-site-scripting operator), 'deserialization' \u2192 unsafe-deserialization preamble denylist (node-serialize _$$ND_FUNC$$_, Java rO0 base64, PHP O:<n>:, python pickle \\x80 opcode framing), 'ai-prompt' \u2192 LLM prompt-injection heuristic denylist (jailbreak / system-prompt-leak / role-override markers). Per-arg and explicit \u2014 replaces the implicit string-type heuristic. 'sql'/'nosql'/'os-command'/'xpath'/'ldap'/'code-eval'/'xss'/'deserialization' are attributed to SSEC-INJECTION; 'ai-prompt' is attributed to the distinct x-security-native SSEC-PROMPT class (one synthetic id per threat class). Never an OWASP-API cell. Edge-enforceable on coraza/bunkerweb only; kong/envoy resolve unsupported.",
|
|
25801
25801
|
type: "array",
|
|
25802
25802
|
minItems: 1,
|
|
25803
25803
|
uniqueItems: true,
|
|
@@ -25878,7 +25878,7 @@ var init_x_security_schema = __esm({
|
|
|
25878
25878
|
}
|
|
25879
25879
|
},
|
|
25880
25880
|
DataAtRest: {
|
|
25881
|
-
description: "v0.8 (deferred-residuals, SSEC-STORAGE): ADVISORY-DECLARATION ONLY. Names body fields (e.g. password, pan, cvv, apiKey) and the asserted at-rest protection posture the app is SUPPOSED to apply. This field is NOT gateway-enforced and compiles to NOTHING enforcing \u2014 the gateway structurally never sees the DB write, so it cannot encrypt/hash/tokenize at rest. Its capability is HARD-PINNED to override-only/unsupported on EVERY target (never full, never partial). It exists ONLY to (1) document declared posture as a comment/annotation in the compiled output, and (2) drive an out-of-band
|
|
25881
|
+
description: "v0.8 (deferred-residuals, SSEC-STORAGE): ADVISORY-DECLARATION ONLY. Names body fields (e.g. password, pan, cvv, apiKey) and the asserted at-rest protection posture the app is SUPPOSED to apply. This field is NOT gateway-enforced and compiles to NOTHING enforcing \u2014 the gateway structurally never sees the DB write, so it cannot encrypt/hash/tokenize at rest. Its capability is HARD-PINNED to override-only/unsupported on EVERY target (never full, never partial). It exists ONLY to (1) document declared posture as a comment/annotation in the compiled output, and (2) drive an out-of-band x-security SCAN/agent finding under SSEC-STORAGE when the declared posture is absent or contradicted by detected plaintext storage. A finding, not a control.",
|
|
25882
25882
|
type: "object",
|
|
25883
25883
|
required: ["fields", "protection"],
|
|
25884
25884
|
additionalProperties: false,
|
|
@@ -25898,7 +25898,7 @@ var init_x_security_schema = __esm({
|
|
|
25898
25898
|
}
|
|
25899
25899
|
},
|
|
25900
25900
|
Logging: {
|
|
25901
|
-
description: "v0.7 (SSEC-AUDIT): declarative audit/access logging policy. The gateway IS the log point, so this compiles natively per target: coraza SecAuditLog/SecAuditLogParts + per-event tagged log actions, bunkerweb modsec audit config, kong http-log/file-log/tcp-log plugins, envoy access_log with a JSON format string.
|
|
25901
|
+
description: "v0.7 (SSEC-AUDIT): declarative audit/access logging policy. The gateway IS the log point, so this compiles natively per target: coraza SecAuditLog/SecAuditLogParts + per-event tagged log actions, bunkerweb modsec audit config, kong http-log/file-log/tcp-log plugins, envoy access_log with a JSON format string. x-security-native (OWASP-API-2023 retired the dedicated logging class, so this is attributed to SSEC-AUDIT, never an API cell).",
|
|
25902
25902
|
type: "object",
|
|
25903
25903
|
required: ["events"],
|
|
25904
25904
|
additionalProperties: false,
|
|
@@ -26129,7 +26129,7 @@ var init_x_security_schema = __esm({
|
|
|
26129
26129
|
}
|
|
26130
26130
|
},
|
|
26131
26131
|
GraphqlOperation: {
|
|
26132
|
-
description: "v0.8 (deferred-residuals): per-OPERATION GraphQL policy bound to a single named field/operation (e.g. systemDiagnostics, pastes, importPaste) that the one POST /graphql route funnels. authz reuses the Authorization rule grammar (rule-based jwt.sub==resource.owner for per-resolver BOLA \u2192 API1:2023; rbac for per-resolver BFLA \u2192 API5:2023). Per-operation maxDepth/maxComplexity/maxAliases override the block-level coarse limits (API4:2023). OVERRIDE-ONLY on every target: a gateway physically cannot evaluate per-resolver authz or per-operation cost without parsing the GraphQL query+variables out of the POST body and resolving the selection set \u2014 the SAME GraphQL-aware-processor gap as the response-schema ext_proc.
|
|
26132
|
+
description: "v0.8 (deferred-residuals): per-OPERATION GraphQL policy bound to a single named field/operation (e.g. systemDiagnostics, pastes, importPaste) that the one POST /graphql route funnels. authz reuses the Authorization rule grammar (rule-based jwt.sub==resource.owner for per-resolver BOLA \u2192 API1:2023; rbac for per-resolver BFLA \u2192 API5:2023). Per-operation maxDepth/maxComplexity/maxAliases override the block-level coarse limits (API4:2023). OVERRIDE-ONLY on every target: a gateway physically cannot evaluate per-resolver authz or per-operation cost without parsing the GraphQL query+variables out of the POST body and resolving the selection set \u2014 the SAME GraphQL-aware-processor gap as the response-schema ext_proc. x-security ships SCAFFOLDING (an ext_proc filter + a graphql-operations.json data file and/or a graphql-armor/Apollo-router config artifact); ENFORCEMENT DEPENDS ON the operator supplying the GraphQL-aware processor. Until they do, NOTHING here is enforced \u2014 therefore override-only, NOT full, NOT partial. The compiled artifact's enforcement status reads 'override-only' verbatim.",
|
|
26133
26133
|
type: "object",
|
|
26134
26134
|
required: ["name"],
|
|
26135
26135
|
additionalProperties: false,
|
|
@@ -26145,7 +26145,7 @@ var init_x_security_schema = __esm({
|
|
|
26145
26145
|
enum: ["query", "mutation", "subscription"]
|
|
26146
26146
|
},
|
|
26147
26147
|
authz: {
|
|
26148
|
-
description: "Per-resolver authorization. Mirrors the top-level Authorization $def grammar (rbac / rule-based / abac). Presence attributes to API1:2023 (per-resolver BOLA) and API5:2023 (per-resolver BFLA). OVERRIDE-ONLY \u2014 requires an operator-supplied GraphQL-aware processor;
|
|
26148
|
+
description: "Per-resolver authorization. Mirrors the top-level Authorization $def grammar (rbac / rule-based / abac). Presence attributes to API1:2023 (per-resolver BOLA) and API5:2023 (per-resolver BFLA). OVERRIDE-ONLY \u2014 requires an operator-supplied GraphQL-aware processor; x-security emits only the rule data + scaffolding, never an enforcing filter.",
|
|
26149
26149
|
$ref: "#/$defs/Authorization"
|
|
26150
26150
|
},
|
|
26151
26151
|
maxDepth: { type: "integer", minimum: 1, maximum: 50 },
|
|
@@ -26326,19 +26326,19 @@ var init_owasp_mapping = __esm({
|
|
|
26326
26326
|
mitigatedBy: ["mtls", "request.schema.domainAllowlist", "request.signature", "request.allowedHosts"]
|
|
26327
26327
|
},
|
|
26328
26328
|
"SSEC-INJECTION": {
|
|
26329
|
-
name: "Injection (
|
|
26329
|
+
name: "Injection (x-security-native)",
|
|
26330
26330
|
mitigatedBy: ["request.schema.injectionGuard"]
|
|
26331
26331
|
},
|
|
26332
26332
|
"SSEC-PROMPT": {
|
|
26333
|
-
name: "LLM Prompt Injection (
|
|
26333
|
+
name: "LLM Prompt Injection (x-security-native)",
|
|
26334
26334
|
mitigatedBy: ["request.schema.injectionGuard"]
|
|
26335
26335
|
},
|
|
26336
26336
|
"SSEC-AUDIT": {
|
|
26337
|
-
name: "Audit Logging (
|
|
26337
|
+
name: "Audit Logging (x-security-native)",
|
|
26338
26338
|
mitigatedBy: ["logging"]
|
|
26339
26339
|
},
|
|
26340
26340
|
"SSEC-STORAGE": {
|
|
26341
|
-
name: "At-Rest Storage Posture (
|
|
26341
|
+
name: "At-Rest Storage Posture (x-security-native, advisory-only)",
|
|
26342
26342
|
mitigatedBy: ["request.dataAtRest"]
|
|
26343
26343
|
}
|
|
26344
26344
|
};
|
|
@@ -62903,7 +62903,7 @@ ${plan.yaml}`,
|
|
|
62903
62903
|
endpoint: "(gateway)",
|
|
62904
62904
|
rule: "load-coverage",
|
|
62905
62905
|
verdict: "FAIL",
|
|
62906
|
-
message: "
|
|
62906
|
+
message: "x-security-emitted artifacts are not loaded by the gateway (xsecurity verify reported <90%). Aborting traffic phase \u2014 the results would be unattributable.",
|
|
62907
62907
|
durationMs: 0
|
|
62908
62908
|
});
|
|
62909
62909
|
} else {
|
|
@@ -63296,7 +63296,7 @@ var init_feasibility = __esm({
|
|
|
63296
63296
|
]
|
|
63297
63297
|
};
|
|
63298
63298
|
CAPKEY_DISCLAIMERS = {
|
|
63299
|
-
"graphql.operations.authz": "override-only: per-operation GraphQL authz enforcement depends on an operator-supplied GraphQL-aware processor;
|
|
63299
|
+
"graphql.operations.authz": "override-only: per-operation GraphQL authz enforcement depends on an operator-supplied GraphQL-aware processor; x-security emits scaffolding only",
|
|
63300
63300
|
"graphql.staticLimits": "override-only: coarse GraphQL cost limits depend on an operator-supplied GraphQL-aware processor; a non-parsing crude limit is partial at best",
|
|
63301
63301
|
"request.serializeBy": "edge serialization only \u2014 does NOT provide in-handler transaction atomicity",
|
|
63302
63302
|
"request.dataAtRest": "advisory posture declaration \u2014 NOT gateway-enforced; drives an out-of-band SSEC-STORAGE scan finding"
|
|
@@ -64034,7 +64034,7 @@ async function checkEndpoint(input, fetcher = defaultFetcher) {
|
|
|
64034
64034
|
const res = await fetcher(url, {
|
|
64035
64035
|
headers: {
|
|
64036
64036
|
Authorization: `Bearer ${apiKey}`,
|
|
64037
|
-
"User-Agent": "@
|
|
64037
|
+
"User-Agent": "@x-security/cursor-mcp/0.1.0",
|
|
64038
64038
|
Accept: "application/json"
|
|
64039
64039
|
}
|
|
64040
64040
|
});
|
|
@@ -64276,7 +64276,7 @@ async function runMcp() {
|
|
|
64276
64276
|
const mod = await Promise.resolve().then(() => (init_dist5(), dist_exports));
|
|
64277
64277
|
const main2 = mod.main ?? mod.default;
|
|
64278
64278
|
if (typeof main2 !== "function") {
|
|
64279
|
-
throw new Error("@
|
|
64279
|
+
throw new Error("@x-security/cursor-mcp did not export a main()/default function");
|
|
64280
64280
|
}
|
|
64281
64281
|
await main2();
|
|
64282
64282
|
return { exitCode: 0 };
|
|
@@ -67839,7 +67839,7 @@ function flaskMethods(tail) {
|
|
|
67839
67839
|
return verbs.length > 0 ? verbs : ["GET"];
|
|
67840
67840
|
}
|
|
67841
67841
|
function stripPyComments(text) {
|
|
67842
|
-
return text.split("\n").
|
|
67842
|
+
return text.split("\n").map((line) => line.trimStart().startsWith("#") ? "" : line).join("\n");
|
|
67843
67843
|
}
|
|
67844
67844
|
function lineAt(text, index) {
|
|
67845
67845
|
let line = 1;
|
|
@@ -68970,7 +68970,7 @@ function lineAt8(text, index) {
|
|
|
68970
68970
|
return line;
|
|
68971
68971
|
}
|
|
68972
68972
|
function stripPyComments4(text) {
|
|
68973
|
-
return text.split("\n").
|
|
68973
|
+
return text.split("\n").map((line) => line.trimStart().startsWith("#") ? "" : line).join("\n");
|
|
68974
68974
|
}
|
|
68975
68975
|
async function parseGraphene(repoDir) {
|
|
68976
68976
|
const routes = [];
|
|
@@ -69038,8 +69038,8 @@ function opBlockRe(type) {
|
|
|
69038
69038
|
return new RegExp(`type\\s+${type}\\s*\\{([^}]*)\\}`, "g");
|
|
69039
69039
|
}
|
|
69040
69040
|
function stripJsComments(text) {
|
|
69041
|
-
const noBlocks = text.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
69042
|
-
return noBlocks.split("\n").
|
|
69041
|
+
const noBlocks = text.replace(/\/\*[\s\S]*?\*\//g, (m3) => m3.replace(/[^\n]/g, " "));
|
|
69042
|
+
return noBlocks.split("\n").map((line) => line.trimStart().startsWith("//") ? "" : line).join("\n");
|
|
69043
69043
|
}
|
|
69044
69044
|
function lineAt9(text, index) {
|
|
69045
69045
|
let line = 1;
|
|
@@ -69150,15 +69150,19 @@ function lineAt10(text, index) {
|
|
|
69150
69150
|
function stripComments2(text, lang) {
|
|
69151
69151
|
let t = text;
|
|
69152
69152
|
if (lang === "js") {
|
|
69153
|
-
t = t.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
69153
|
+
t = t.replace(/\/\*[\s\S]*?\*\//g, (m3) => m3.replace(/[^\n]/g, " "));
|
|
69154
69154
|
}
|
|
69155
69155
|
const out = [];
|
|
69156
69156
|
for (let line of t.split("\n")) {
|
|
69157
69157
|
const s = line.trimStart();
|
|
69158
|
-
if (lang === "js" && s.startsWith("//"))
|
|
69158
|
+
if (lang === "js" && s.startsWith("//")) {
|
|
69159
|
+
out.push("");
|
|
69159
69160
|
continue;
|
|
69160
|
-
|
|
69161
|
+
}
|
|
69162
|
+
if (lang === "py" && s.startsWith("#")) {
|
|
69163
|
+
out.push("");
|
|
69161
69164
|
continue;
|
|
69165
|
+
}
|
|
69162
69166
|
if (lang === "js") {
|
|
69163
69167
|
line = line.replace(/(^|[^:'"])\/\/.*$/, "$1");
|
|
69164
69168
|
}
|
|
@@ -73141,7 +73145,7 @@ var init_push = __esm({
|
|
|
73141
73145
|
DEFAULT_API_URL2 = "https://usewaf.com";
|
|
73142
73146
|
HOST_ALLOWLIST_SUFFIXES = [".chain305.com", ".lazy.chain305.com"];
|
|
73143
73147
|
LOCAL_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
73144
|
-
USER_AGENT = "@
|
|
73148
|
+
USER_AGENT = "@x-security/cli push";
|
|
73145
73149
|
REPO_URL_RE = /^https:\/\/github\.com\/[^/\s]+\/[^/\s]+(\.git)?$/i;
|
|
73146
73150
|
PushError = class extends Error {
|
|
73147
73151
|
};
|
|
@@ -73436,7 +73440,7 @@ var init_ci_gate = __esm({
|
|
|
73436
73440
|
"#!/usr/bin/env bash",
|
|
73437
73441
|
"# Fail CI if any emitted control lost its byte-matching citation (Rule D-3).",
|
|
73438
73442
|
"set -euo pipefail",
|
|
73439
|
-
'OUT="$(npx --yes @
|
|
73443
|
+
'OUT="$(npx --yes @x-security/cli audit .)"',
|
|
73440
73444
|
'echo "$OUT"',
|
|
73441
73445
|
`CITE_BACKED="$(printf '%s' "$OUT" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>process.stdout.write(String(JSON.parse(s).citeBacked)))')"`,
|
|
73442
73446
|
'if [ "$CITE_BACKED" != "true" ]; then',
|
|
@@ -75247,7 +75251,7 @@ Examples:
|
|
|
75247
75251
|
process.exit(1);
|
|
75248
75252
|
}
|
|
75249
75253
|
});
|
|
75250
|
-
program.command("mcp").description("Run the
|
|
75254
|
+
program.command("mcp").description("Run the x-security Cursor MCP server (stdio JSON-RPC). For use in .cursor/mcp.json.").action(async () => {
|
|
75251
75255
|
try {
|
|
75252
75256
|
const r5 = await runMcp();
|
|
75253
75257
|
process.exit(r5.exitCode);
|
|
@@ -75257,7 +75261,7 @@ Examples:
|
|
|
75257
75261
|
process.exit(1);
|
|
75258
75262
|
}
|
|
75259
75263
|
});
|
|
75260
|
-
program.command("verify-bundle <tarball>").description("Verify a signed
|
|
75264
|
+
program.command("verify-bundle <tarball>").description("Verify a signed x-security release bundle (sha256 file hashes + Ed25519 manifest signature).").option("--public-key <path>", "Path to an Ed25519 SPKI PEM public key. Defaults to the embedded release key.").action(async (tarball, opts) => {
|
|
75261
75265
|
try {
|
|
75262
75266
|
const verifyOpts = {};
|
|
75263
75267
|
if (opts.publicKey !== void 0) verifyOpts.publicKeyPath = opts.publicKey;
|
|
@@ -75314,7 +75318,7 @@ Examples:
|
|
|
75314
75318
|
});
|
|
75315
75319
|
registerExtras(program);
|
|
75316
75320
|
program.command("push <repoDir>").description(
|
|
75317
|
-
"Upload the verified .writ/ policies to the
|
|
75321
|
+
"Upload the verified .writ/ policies to the x-security SaaS. Aborts if the local audit is not 100% cite-backed. Token from WRIT_API_TOKEN env only."
|
|
75318
75322
|
).option("--dry-run", "Assemble + validate the payload and print a summary WITHOUT sending.").action(async (repoDir, opts) => {
|
|
75319
75323
|
try {
|
|
75320
75324
|
const pushOpts = {};
|
package/package.json
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chain305/x-security",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.3",
|
|
4
|
+
"description": "x-security CLI — generate/validate/test/report/diff/init against multiple gateway targets",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://usewaf.com",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/
|
|
10
|
-
"directory": "packages/cli"
|
|
9
|
+
"url": "git+https://github.com/ZeeshanSultan/x-security.git"
|
|
11
10
|
},
|
|
12
11
|
"type": "module",
|
|
13
12
|
"bin": {
|