fullcourtdefense-cli 1.34.21 → 1.34.25
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 +1 -0
- package/dist/actionIdentity.d.ts +1 -0
- package/dist/actionIdentity.js +2 -0
- package/dist/actionPolicyEngine.d.ts +5 -0
- package/dist/actionPolicyEngine.js +273 -30
- package/dist/agentTerminal.d.ts +1 -3
- package/dist/agentTerminal.js +14 -29
- package/dist/approvalPrompt.d.ts +7 -0
- package/dist/approvalPrompt.js +46 -0
- package/dist/askDialog.d.ts +87 -0
- package/dist/askDialog.js +209 -0
- package/dist/blockExplanation.js +18 -6
- package/dist/boundaryVerification.d.ts +20 -0
- package/dist/boundaryVerification.js +147 -0
- package/dist/commands/daemon.js +198 -70
- package/dist/commands/deterministicGuard.d.ts +2 -0
- package/dist/commands/deterministicGuard.js +161 -9
- package/dist/commands/hook.js +81 -29
- package/dist/commands/installClaudeHook.js +11 -5
- package/dist/commands/installCursorHook.js +5 -4
- package/dist/commands/mcpGateway.d.ts +2 -0
- package/dist/commands/mcpGateway.js +163 -56
- package/dist/commands/onboard.js +6 -5
- package/dist/commands/onboardingSummary.d.ts +5 -0
- package/dist/commands/onboardingSummary.js +11 -0
- package/dist/commands/protectedRun.d.ts +14 -0
- package/dist/commands/protectedRun.js +207 -0
- package/dist/containment-runtime/index.mjs +30118 -0
- package/dist/containment-runtime/licenses/_anthropic-ai_sandbox-runtime.txt +201 -0
- package/dist/containment-runtime/licenses/_pondwader_socks5-server.txt +20 -0
- package/dist/containment-runtime/licenses/commander.txt +22 -0
- package/dist/containment-runtime/licenses/node-forge.txt +331 -0
- package/dist/containment-runtime/licenses/zod.txt +21 -0
- package/dist/containment-runtime/runtime-version.json +1 -0
- package/dist/containment-runtime/vendor/java-proxy-agent/build.ts +95 -0
- package/dist/containment-runtime/vendor/java-proxy-agent/srt-proxy-agent.jar +0 -0
- package/dist/containment-runtime/vendor/seccomp/arm64/apply-seccomp +0 -0
- package/dist/containment-runtime/vendor/seccomp/build.ts +69 -0
- package/dist/containment-runtime/vendor/seccomp/x64/apply-seccomp +0 -0
- package/dist/containment-runtime/vendor/srt-win/arm64/srt-win.exe +0 -0
- package/dist/containment-runtime/vendor/srt-win/build.ts +21 -0
- package/dist/containment-runtime/vendor/srt-win/x64/srt-win.exe +0 -0
- package/dist/containmentLease.d.ts +5 -0
- package/dist/containmentLease.js +72 -0
- package/dist/detectionUpdates.d.ts +23 -3
- package/dist/detectionUpdates.js +45 -14
- package/dist/detectorArtifact.d.ts +17 -0
- package/dist/detectorArtifact.js +35 -0
- package/dist/detectorBaseline.d.ts +2 -0
- package/dist/detectorBaseline.js +5 -0
- package/dist/detectorModule.d.ts +9 -0
- package/dist/detectorModule.js +84 -0
- package/dist/detectorRuntime.d.ts +21 -0
- package/dist/detectorRuntime.js +49 -0
- package/dist/detectorUpdates.d.ts +21 -0
- package/dist/detectorUpdates.js +86 -0
- package/dist/devConfirm.d.ts +97 -15
- package/dist/devConfirm.js +447 -117
- package/dist/distress.d.ts +2 -0
- package/dist/distress.js +2 -0
- package/dist/frictionRollout.js +103 -0
- package/dist/gatewayRuntime.d.ts +19 -0
- package/dist/gatewayRuntime.js +93 -0
- package/dist/hookSlim.js +17 -18
- package/dist/index.js +35 -0
- package/dist/localDetectionUpdates.d.ts +3 -1
- package/dist/localDetectionUpdates.js +8 -1
- package/dist/localSafetySnapshot.d.ts +9 -5
- package/dist/localSafetySnapshot.js +42 -15
- package/dist/mcpToolReview.d.ts +17 -0
- package/dist/mcpToolReview.js +127 -0
- package/dist/mcpToolTrust.d.ts +22 -0
- package/dist/mcpToolTrust.js +66 -0
- package/dist/nativeHook.d.ts +4 -0
- package/dist/nativeHook.js +31 -17
- package/dist/policyRefresh.d.ts +3 -0
- package/dist/policyRefresh.js +27 -0
- package/dist/protectionMaintenance.d.ts +10 -0
- package/dist/protectionMaintenance.js +64 -0
- package/dist/protectionProfile.d.ts +26 -0
- package/dist/protectionProfile.js +63 -0
- package/dist/runtimeConfig.d.ts +2 -0
- package/dist/runtimeConfig.js +65 -26
- package/dist/selfTest.d.ts +2 -0
- package/dist/selfTest.js +9 -1
- package/dist/sessionLimits.d.ts +2 -0
- package/dist/sessionLimits.js +24 -0
- package/dist/telemetry.d.ts +5 -0
- package/dist/telemetry.js +8 -0
- package/dist/version.json +1 -1
- package/package.json +10 -6
package/README.md
CHANGED
|
@@ -73,6 +73,7 @@ fullcourtdefense init
|
|
|
73
73
|
- `fullcourtdefense help` — shows the full onboarding flow and command reference.
|
|
74
74
|
- `fullcourtdefense onboard --token <token>` — resumable machine transaction: preflight + login + protection + optional discovery + verification. `--json true` prints final machine-readable status; exits non-zero only when a critical step fails.
|
|
75
75
|
- `fullcourtdefense doctor` — confirms outbound HTTPS to FullCourtDefense is open before scanning.
|
|
76
|
+
- `fullcourtdefense hotfix-status` — shows the loaded detector revision and whether fallback is active. Available from 1.34.22. Updates arrive through the background daemon; this command reads local status and does not force a download. Acceptance alone does not prove that a reported false alarm was corrected.
|
|
76
77
|
- `fullcourtdefense login --token <token>` — enrolls this machine with a fleet token and saves per-machine Shield credentials (no copy/paste).
|
|
77
78
|
- `fullcourtdefense install-all` — wraps every configured MCP server, installs IDE hooks and terminal guards, uploads discovery, schedules daily rescans.
|
|
78
79
|
- `fullcourtdefense configure` — legacy/manual setup: saves org API key, Shield ID, Shield key, and API URL to `.fullcourtdefense.yml` (use `login` instead when you have a fleet token).
|
package/dist/actionIdentity.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export interface ActionIdentity {
|
|
|
3
3
|
sessionId?: string;
|
|
4
4
|
runId?: string;
|
|
5
5
|
instanceId?: string;
|
|
6
|
+
attemptId?: string;
|
|
6
7
|
}
|
|
7
8
|
export declare function identityPart(value: unknown): string | undefined;
|
|
8
9
|
export declare function captureActionIdentity(payload?: Record<string, unknown>, env?: NodeJS.ProcessEnv): ActionIdentity;
|
package/dist/actionIdentity.js
CHANGED
|
@@ -11,5 +11,7 @@ function captureActionIdentity(payload = {}, env = process.env) {
|
|
|
11
11
|
|| env.FCD_SESSION_ID || env.CLAUDE_CODE_SESSION_ID || env.CODEX_THREAD_ID),
|
|
12
12
|
runId: identityPart(env.FCD_RUN_ID || env.GITHUB_RUN_ID || env.CI_PIPELINE_ID),
|
|
13
13
|
instanceId: identityPart(env.FCD_WORKLOAD_INSTANCE_ID),
|
|
14
|
+
// Never use a generic request id: some clients reuse it for unrelated calls.
|
|
15
|
+
attemptId: identityPart(payload.tool_use_id || payload.tool_call_id),
|
|
14
16
|
};
|
|
15
17
|
}
|
|
@@ -234,5 +234,10 @@ export declare function inferToolContext(toolName: string, args: Record<string,
|
|
|
234
234
|
operation: string;
|
|
235
235
|
context: Record<string, string>;
|
|
236
236
|
};
|
|
237
|
+
/** Exported for reproducible detector artifacts; policy decisions are not packaged. */
|
|
238
|
+
export declare function inferBundledToolContext(toolName: string, args: Record<string, any>): {
|
|
239
|
+
operation: string;
|
|
240
|
+
context: Record<string, string>;
|
|
241
|
+
};
|
|
237
242
|
/** Static inventory match — same operation rules as runtime, without constraint context. */
|
|
238
243
|
export declare function inventoryToolMatchesActionPolicy(toolName: string, toolActions: string[] | undefined, policies: EngineActionPolicy[]): boolean;
|
|
@@ -29,6 +29,7 @@ __export(actionPolicyEngine_exports, {
|
|
|
29
29
|
detectSecretKind: () => detectSecretKind,
|
|
30
30
|
effectiveApprovalScope: () => effectiveApprovalScope,
|
|
31
31
|
evaluateActionPolicies: () => evaluateActionPolicies,
|
|
32
|
+
inferBundledToolContext: () => inferBundledToolContext,
|
|
32
33
|
inferToolContext: () => inferToolContext,
|
|
33
34
|
inventoryToolMatchesActionPolicy: () => inventoryToolMatchesActionPolicy,
|
|
34
35
|
isLocalFileContentAction: () => isLocalFileContentAction,
|
|
@@ -5740,6 +5741,7 @@ function parse3(input, options) {
|
|
|
5740
5741
|
|
|
5741
5742
|
// src/actionPolicyEngine.ts
|
|
5742
5743
|
var import_detectionData = require("./detectionData");
|
|
5744
|
+
var import_detectorRuntime = require("./detectorRuntime");
|
|
5743
5745
|
function isFirestoreQueryUrl(target) {
|
|
5744
5746
|
try {
|
|
5745
5747
|
const url = new URL(target);
|
|
@@ -5790,6 +5792,27 @@ function provenJavaScriptReadUrls(code) {
|
|
|
5790
5792
|
}
|
|
5791
5793
|
case "EmptyStatement":
|
|
5792
5794
|
return literal2(void 0);
|
|
5795
|
+
case "IfStatement": {
|
|
5796
|
+
ev(node.test);
|
|
5797
|
+
evaluate(node.consequent, new Map(env), depth + 1);
|
|
5798
|
+
if (node.alternate) evaluate(node.alternate, new Map(env), depth + 1);
|
|
5799
|
+
return literal2(void 0);
|
|
5800
|
+
}
|
|
5801
|
+
case "UnaryExpression": {
|
|
5802
|
+
if (node.operator !== "!") return fail();
|
|
5803
|
+
ev(node.argument);
|
|
5804
|
+
return { kind: "data" };
|
|
5805
|
+
}
|
|
5806
|
+
case "ThrowStatement":
|
|
5807
|
+
return ev(node.argument);
|
|
5808
|
+
case "TemplateLiteral": {
|
|
5809
|
+
for (const expression of node.expressions) {
|
|
5810
|
+
const value = ev(expression);
|
|
5811
|
+
if (!["literal", "data", "string"].includes(value.kind)) return fail();
|
|
5812
|
+
if (value.kind === "literal" && value.value !== null && typeof value.value === "object") return fail();
|
|
5813
|
+
}
|
|
5814
|
+
return { kind: "string" };
|
|
5815
|
+
}
|
|
5793
5816
|
case "ExpressionStatement":
|
|
5794
5817
|
case "AwaitExpression":
|
|
5795
5818
|
case "ChainExpression":
|
|
@@ -5833,6 +5856,7 @@ function provenJavaScriptReadUrls(code) {
|
|
|
5833
5856
|
case "BinaryExpression": {
|
|
5834
5857
|
const left = ev(node.left);
|
|
5835
5858
|
const right = ev(node.right);
|
|
5859
|
+
if (node.operator === "===" || node.operator === "!==") return { kind: "data" };
|
|
5836
5860
|
if (node.operator !== "+") return fail();
|
|
5837
5861
|
if (left.kind === "literal" && right.kind === "literal" && typeof left.value === "string" && typeof right.value === "string") {
|
|
5838
5862
|
if (left.value.length + right.value.length > 32768) return fail();
|
|
@@ -5868,6 +5892,12 @@ function provenJavaScriptReadUrls(code) {
|
|
|
5868
5892
|
}
|
|
5869
5893
|
case "NewExpression": {
|
|
5870
5894
|
const constructor = ev(node.callee);
|
|
5895
|
+
if (constructor.kind === "call" && constructor.name === "Error") {
|
|
5896
|
+
if (node.arguments.length !== 1) return fail();
|
|
5897
|
+
const message = ev(node.arguments[0]);
|
|
5898
|
+
if (message.kind !== "string" && !(message.kind === "literal" && typeof message.value === "string")) return fail();
|
|
5899
|
+
return { kind: "data" };
|
|
5900
|
+
}
|
|
5871
5901
|
if (constructor.kind !== "call" || constructor.name !== "GoogleAuth" || node.arguments.length !== 1) return fail();
|
|
5872
5902
|
const options = ev(node.arguments[0]);
|
|
5873
5903
|
if (options.kind !== "object" || options.fields.size !== 1) return fail();
|
|
@@ -5998,6 +6028,7 @@ function provenJavaScriptReadUrls(code) {
|
|
|
5998
6028
|
evaluate(tree, /* @__PURE__ */ new Map([
|
|
5999
6029
|
["fetch", callable("fetch")],
|
|
6000
6030
|
["require", callable("require")],
|
|
6031
|
+
["Error", callable("Error")],
|
|
6001
6032
|
["JSON", object([["stringify", callable("JSON.stringify")]])],
|
|
6002
6033
|
["console", object([["log", callable("console.log")], ["error", callable("console.error")]])]
|
|
6003
6034
|
]));
|
|
@@ -6738,14 +6769,17 @@ var OPERATION_MATCH_SKIP_FIELDS = /* @__PURE__ */ new Set([
|
|
|
6738
6769
|
"recipients",
|
|
6739
6770
|
"from",
|
|
6740
6771
|
"cc",
|
|
6741
|
-
"bcc"
|
|
6772
|
+
"bcc",
|
|
6773
|
+
"domain",
|
|
6774
|
+
"destinationType",
|
|
6775
|
+
"toolStatus"
|
|
6742
6776
|
]);
|
|
6743
6777
|
function buildOperationMatchText(_toolName, context) {
|
|
6744
6778
|
const parts = [];
|
|
6745
6779
|
for (const [key, value] of Object.entries(context)) {
|
|
6746
6780
|
const fieldWords = key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().split(/[_.-]/);
|
|
6747
6781
|
const identifierField = /^(?:id|ids|uuid|uuids|guid|guids)$/.test(fieldWords[fieldWords.length - 1]);
|
|
6748
|
-
if (OPERATION_MATCH_SKIP_FIELDS.has(key) || identifierField) continue;
|
|
6782
|
+
if (OPERATION_MATCH_SKIP_FIELDS.has(key) || identifierField || key.startsWith("args.") || key.startsWith("request.")) continue;
|
|
6749
6783
|
if (typeof value !== "string" || value.length === 0 || value.length > 2e3) continue;
|
|
6750
6784
|
const text = COMMAND_LINE_FIELDS.has(key) ? shellVerbWords(value) : value;
|
|
6751
6785
|
parts.push(`${key} ${text}`);
|
|
@@ -6818,6 +6852,7 @@ function isHighEntropySecretToken(token, allowWordSlug = false) {
|
|
|
6818
6852
|
if (/^sha(?:1|256|384|512)-/i.test(token)) return false;
|
|
6819
6853
|
if (/^data:/i.test(token) || /^[A-Za-z0-9+/]{200,}={0,2}$/.test(token)) return false;
|
|
6820
6854
|
if (/^(?:https?|s3|gs|file):\/\//i.test(token) || /[\\/]{1}[\w.-]+[\\/]/.test(token)) return false;
|
|
6855
|
+
if (allowWordSlug && /^[A-Za-z_][\w.-]*\/[A-Za-z_][\w.-]*\.[A-Za-z][A-Za-z0-9]{0,9}$/.test(token)) return false;
|
|
6821
6856
|
if (/^[a-z]+(?:[A-Z][a-z0-9]+)+$/.test(token) || /^[a-z]+(?:_[a-z0-9]+)+$/.test(token)) return false;
|
|
6822
6857
|
if (allowWordSlug && /^[A-Z][a-z]+-(?:[A-Z][a-z]+){2,}$/.test(token)) return false;
|
|
6823
6858
|
if (allowWordSlug && /^[a-z]+(?:-[a-z]+)+(?:-\d+)?$/.test(token)) return false;
|
|
@@ -6988,8 +7023,15 @@ function explicitGitHubWriteRepository(command) {
|
|
|
6988
7023
|
function withoutUnattributedJavaScriptUrls(segment) {
|
|
6989
7024
|
if (segment.length > 32768) return segment;
|
|
6990
7025
|
const inline = /^\s*node(?:\.exe)?\s+(?:-e|--eval)\s+(?:"([^"$`\\]*)"|'([^']*)')\s*$/i.exec(segment);
|
|
6991
|
-
|
|
6992
|
-
|
|
7026
|
+
let code;
|
|
7027
|
+
if (inline) code = inline[1] ?? inline[2];
|
|
7028
|
+
else {
|
|
7029
|
+
const raw = segment.trim();
|
|
7030
|
+
const opening = /^@'\r?\n/.exec(raw);
|
|
7031
|
+
const end = opening ? hereStringEnd(raw, 0) : -1;
|
|
7032
|
+
if (!opening || end < 0 || !/^\s*\|\s*node(?:\.exe)?(?:\s+-)?\s*$/i.test(raw.slice(end))) return segment;
|
|
7033
|
+
code = raw.slice(opening[0].length, end - 3);
|
|
7034
|
+
}
|
|
6993
7035
|
let budget = 4e3;
|
|
6994
7036
|
try {
|
|
6995
7037
|
const tree = parse3(code, { ecmaVersion: "latest", sourceType: "script", allowAwaitOutsideFunction: true });
|
|
@@ -7051,8 +7093,12 @@ function extractUrl(args, _argsText) {
|
|
|
7051
7093
|
}
|
|
7052
7094
|
function shellUrlActionText(command) {
|
|
7053
7095
|
if (command.length > 65536) return command;
|
|
7096
|
+
if (/\$env:(?:TEMP|TMP)\s*=/i.test(maskQuotedSpans(command))) return command;
|
|
7097
|
+
const fileSyntax = (text) => text.replace(/"\$env:(?:TEMP|TMP)([\\/][A-Za-z0-9_ ./\\-]+)"/gi, '"fcd-temporary$1"').replace(/\(Join-Path\s+\$env:(?:TEMP|TMP)\s+'([A-Za-z0-9_ ./\\-]+)'\)/gi, '"fcd-temporary/$1"');
|
|
7054
7098
|
const originalSegments = splitShellSegments(command);
|
|
7055
7099
|
const localPaths = /* @__PURE__ */ new Map();
|
|
7100
|
+
const literalValues = /* @__PURE__ */ new Map();
|
|
7101
|
+
const dataAssignments = /* @__PURE__ */ new Set();
|
|
7056
7102
|
const pathAssignments = /* @__PURE__ */ new Set();
|
|
7057
7103
|
let expandedSize = command.length;
|
|
7058
7104
|
let expansionOverflow = false;
|
|
@@ -7065,6 +7111,11 @@ function shellUrlActionText(command) {
|
|
|
7065
7111
|
return value;
|
|
7066
7112
|
};
|
|
7067
7113
|
const segments = originalSegments.map((segment, index) => {
|
|
7114
|
+
const dataAssignment = /^\$([A-Za-z_][\w]*)\s*=\s*(@'\r?\n[\s\S]*?\r?\n'@|'(?:[^']|'')*')$/.exec(segment);
|
|
7115
|
+
if (dataAssignment && !literalValues.has(dataAssignment[1].toLowerCase()) && literalValues.size < 32) {
|
|
7116
|
+
literalValues.set(dataAssignment[1].toLowerCase(), dataAssignment[2]);
|
|
7117
|
+
dataAssignments.add(index);
|
|
7118
|
+
}
|
|
7068
7119
|
const assignment = /^\$([A-Za-z_][\w]*)\s*=\s*(?:'([^']*)'|"([^"$`]*)")$/.exec(segment);
|
|
7069
7120
|
if (assignment) {
|
|
7070
7121
|
const name = assignment[1].toLowerCase();
|
|
@@ -7086,7 +7137,14 @@ function shellUrlActionText(command) {
|
|
|
7086
7137
|
const value = localPaths.get(name.toLowerCase());
|
|
7087
7138
|
return value ? expand(match, `"${value}"`) : match;
|
|
7088
7139
|
});
|
|
7089
|
-
|
|
7140
|
+
const expanded = prefix + fileSyntax(tail);
|
|
7141
|
+
return expanded.replace(
|
|
7142
|
+
/^(Set-Content|Add-Content|Out-File)(\s+[\s\S]*?\s+-(?:Value|InputObject)\s+)\$([A-Za-z_][\w]*)(\s+-[\s\S]*|\s*)$/i,
|
|
7143
|
+
(match, writer, options, name, suffix) => {
|
|
7144
|
+
const value = literalValues.get(name.toLowerCase());
|
|
7145
|
+
return value ? expand(match, writer + options + value + suffix) : match;
|
|
7146
|
+
}
|
|
7147
|
+
);
|
|
7090
7148
|
});
|
|
7091
7149
|
if (expansionOverflow) return command;
|
|
7092
7150
|
const filtered = segments.map((segment) => {
|
|
@@ -7121,15 +7179,26 @@ function shellUrlActionText(command) {
|
|
|
7121
7179
|
return tail;
|
|
7122
7180
|
});
|
|
7123
7181
|
const onlyStoredData = segments.every((segment, index) => {
|
|
7182
|
+
if (dataAssignments.has(index)) return true;
|
|
7124
7183
|
if (pathAssignments.has(index)) return true;
|
|
7125
7184
|
if (filtered[index] !== segment) return true;
|
|
7185
|
+
if (/^\$[A-Za-z_][\w]*\s*=\s*\$null$/i.test(segment)) return true;
|
|
7186
|
+
if (/^\[System\.Management\.Automation\.Language\.Parser\]::ParseFile\("[^"$`\r\n|;&<>]*",\s*\[ref\]\$[A-Za-z_][\w]*,\s*\[ref\]\$[A-Za-z_][\w]*\)\s*\|\s*Out-Null$/i.test(segment)) return true;
|
|
7187
|
+
if (/^if\s*\(\$([A-Za-z_][\w]*)\.Count\)\s*\{\s*throw\s+\$\1\[0\]\s*\}$/i.test(segment)) return true;
|
|
7188
|
+
if (/^Write-Output\s+'(?:[^']|'')*'$/i.test(segment)) return true;
|
|
7189
|
+
if (/^gh\s+pr\s+checks\s+\d+\s+--json\s+[a-z,]+\s+--jq\s+'(?:[^']|'')*'$/i.test(segment)) return true;
|
|
7190
|
+
const read = /^Get-Content\s+(?:(?:-LiteralPath|-Path)\s+)?(?:'([^'$`\r\n]*)'|"([^"$`\r\n]*)"|([^\s'"$`|;&(){}<>]+))(?:\s+-(?:Tail|TotalCount)\s+\d{1,6})?(?:\s+-Raw)?\s*$/i.exec(segment);
|
|
7191
|
+
if (read) {
|
|
7192
|
+
const target = read[1] ?? read[2] ?? read[3];
|
|
7193
|
+
if (target && !target.startsWith("-") && !/^[\\/]{2}|[\r\n*?\[\]<>]/.test(target) && !/:/.test(target.replace(/^[A-Za-z]:[\\/]/, ""))) return true;
|
|
7194
|
+
}
|
|
7126
7195
|
const directory = /^New-Item\s+-ItemType\s+Directory\s+(?:-Force\s+)?-(?:LiteralPath|Path)\s+(?:'([^']*)'|"([^"$`]*)")\s*(?:\|\s*Out-Null)?$/i.exec(segment);
|
|
7127
7196
|
if (directory && /^(?:[A-Za-z]:[\\/]|\/(?!\/))[\w ./\\-]+$/.test(directory[1] ?? directory[2])) return true;
|
|
7128
7197
|
const words = maskQuotedSpans(segment);
|
|
7129
7198
|
if (!/[$`|;&(){}<>]/.test(segment) && /^(?:(?:cd|pushd|set-location)\s+[^\r\n]+|(?:pwd|popd|get-location)|git\s+status(?:\s+[^\r\n]+)?)$/i.test(segment)) return true;
|
|
7130
7199
|
return !/[$`]/.test(segment) && /^\(Get-Content\s+(?:[A-Za-z]:[\\/])?[\w./\\-]+\s+-Raw\)\.Replace\("",\s*""\)\s*\|\s*Set-Content\s+(?:[A-Za-z]:[\\/])?[\w./\\-]+\s+-Encoding\s+utf8\s*$/i.test(words);
|
|
7131
7200
|
});
|
|
7132
|
-
return onlyStoredData && filtered.some((value, index) => value !== segments[index]) ? filtered.join("\n") : command;
|
|
7201
|
+
return onlyStoredData && filtered.some((value, index) => value !== segments[index]) ? filtered.filter((_, index) => !dataAssignments.has(index)).join("\n") : command;
|
|
7133
7202
|
}
|
|
7134
7203
|
function splitShellSegments(command, alsoOnPipe = false) {
|
|
7135
7204
|
const out = [];
|
|
@@ -7367,14 +7436,22 @@ function shellSegmentLead(segment) {
|
|
|
7367
7436
|
const tokens = segment.trim().split(/\s+/).filter(Boolean);
|
|
7368
7437
|
let i2 = 0;
|
|
7369
7438
|
while (i2 < tokens.length && (/^[A-Za-z_][\w]*=/.test(tokens[i2]) || /^(?:sudo|time|nohup|command|builtin|exec|nice|env)$/i.test(tokens[i2]))) i2++;
|
|
7370
|
-
|
|
7439
|
+
if (i2 < tokens.length && /^\$[\w:]+$/.test(tokens[i2]) && tokens[i2 + 1] === "=") i2 += 2;
|
|
7440
|
+
else if (i2 < tokens.length && /^\$[\w:]+=$/.test(tokens[i2])) i2 += 1;
|
|
7441
|
+
else if (i2 < tokens.length && /^\$[\w:]+=./.test(tokens[i2])) tokens[i2] = tokens[i2].replace(/^\$[\w:]+=/, "");
|
|
7442
|
+
while (i2 < tokens.length && /^(?:\$|@)?\(+$/.test(tokens[i2])) i2++;
|
|
7443
|
+
const lead = (tokens[i2] || "").toLowerCase().replace(/^(?:\$|@)?\(+/, "").replace(/^["']|["']$/g, "");
|
|
7371
7444
|
return lead.replace(/^.*[\\/]/, "").replace(/\.(exe|cmd|bat|ps1)$/i, "");
|
|
7372
7445
|
}
|
|
7446
|
+
var PS_SESSION_VARIABLE_ASSIGN_RE = /^\s*\$(?:psdefaultparametervalues|psmodulepath|profile|executioncontext|env:(?:path|pathext|psmodulepath))\s*(?:\[[^\]]*\]\s*)?(?:\+?=)/i;
|
|
7447
|
+
var PS_VALUE_EXECUTOR_RE = /\b(?:invoke-expression|iex|invoke-command|icm|start-process|saps|invoke-item|ii)\b|&\s*[$(]|\.\s*invoke\s*\(/i;
|
|
7373
7448
|
var worstShellOp = (ops, floor = "read") => ops.reduce((w, op) => (SHELL_OP_RANK[op] || 0) > (SHELL_OP_RANK[w] || 0) ? op : w, floor);
|
|
7374
7449
|
function classifyShellSegment(segment, depth = 0) {
|
|
7375
7450
|
const raw = segment.trim();
|
|
7376
7451
|
if (!raw) return "read";
|
|
7452
|
+
if (PS_SESSION_VARIABLE_ASSIGN_RE.test(raw)) return "SHELL";
|
|
7377
7453
|
const lead = shellSegmentLead(segment);
|
|
7454
|
+
if ((/^\$env:[\w]+$/.test(lead) || /^\$\w+$/.test(lead)) && PS_VALUE_EXECUTOR_RE.test(maskQuotedSpans(raw))) return "SHELL";
|
|
7378
7455
|
if (SHELL_NEUTRAL_BUILTINS.has(lead) || /^\$env:[\w]+$/.test(lead) || /^\$\w+$/.test(lead)) return "read";
|
|
7379
7456
|
const rawLower = raw.toLowerCase();
|
|
7380
7457
|
const sqlClient = SHELL_SQL_CLIENTS.has(lead) || /\|\s*(?:sudo\s+)?(?:psql|mysql|mariadb|sqlcmd|sqlite3|mongosh?|clickhouse-client|bq|cockroach|usql)\b/.test(rawLower);
|
|
@@ -7452,8 +7529,10 @@ function classifyShellWords(masked, raw, lead) {
|
|
|
7452
7529
|
if (/\b(git\s+(?:add|commit|push|merge|rebase|cherry-pick|stash|checkout|switch|restore|tag\s+\S)|npm\s+(?:install|ci|i|uninstall|update|publish)|pnpm\s+(?:install|add|i)|yarn(?:\s+add|\s+install)?|pip3?\s+install|poetry\s+(?:install|add)|cargo\s+(?:build|install)|go\s+(?:build|install|get|mod)|gcloud\s+run\s+deploy|firebase\s+deploy|terraform\s+apply|kubectl\s+apply|docker\s+(?:push|build|compose\s+up)|helm\s+(?:install|upgrade))\b/.test(value)) {
|
|
7453
7530
|
return "write";
|
|
7454
7531
|
}
|
|
7532
|
+
if (SHELL_BLOCK_STRUCTURE_RE.test(value)) return "read";
|
|
7455
7533
|
return "SHELL";
|
|
7456
7534
|
}
|
|
7535
|
+
var SHELL_BLOCK_STRUCTURE_RE = /^(?:\s|[{}()=\-\d]|\+=|""|\$[\w:.]+|\b(?:try|catch|finally|if|then|else|elseif|elif|fi|do|done|esac|end|exit|return|break|continue)\b)*$/i;
|
|
7457
7536
|
function classifyShellCommandOperation(command, depth = 0) {
|
|
7458
7537
|
if (provenPowerShellReadSequence(command)) return "read";
|
|
7459
7538
|
if (shellUrlActionText(command) !== command) return "write";
|
|
@@ -7461,6 +7540,44 @@ function classifyShellCommandOperation(command, depth = 0) {
|
|
|
7461
7540
|
if (segments.length === 0) return "SHELL";
|
|
7462
7541
|
return worstShellOp(segments.map((segment) => classifyShellSegment(segment, depth)), "read");
|
|
7463
7542
|
}
|
|
7543
|
+
function isPowerShellReadFormatter(segment) {
|
|
7544
|
+
if (/^(?:Select-Object\s+[\w., *-]+|Format-Table(?:\s+[\w., *-]+)?|Format-List(?:\s+[\w., *-]+)?|Out-Null|ConvertFrom-Json)$/i.test(segment)) return true;
|
|
7545
|
+
const tokens = segment.trim().split(/\s+/);
|
|
7546
|
+
if (tokens.shift()?.toLowerCase() !== "convertto-json") return false;
|
|
7547
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7548
|
+
for (let i2 = 0; i2 < tokens.length; i2++) {
|
|
7549
|
+
const flag = tokens[i2].toLowerCase();
|
|
7550
|
+
if (seen.has(flag)) return false;
|
|
7551
|
+
seen.add(flag);
|
|
7552
|
+
if (flag === "-compress") continue;
|
|
7553
|
+
if (flag === "-depth" && /^(?:\d{1,2}|100)$/.test(tokens[++i2] || "")) continue;
|
|
7554
|
+
return false;
|
|
7555
|
+
}
|
|
7556
|
+
return true;
|
|
7557
|
+
}
|
|
7558
|
+
function isGcloudStorageCatRead(command) {
|
|
7559
|
+
if (command.length > 8192 || /[$`{}()<>;&|]/.test(command)) return false;
|
|
7560
|
+
const pieces = command.match(/"[^"\r\n]*"|'[^'\r\n]*'|[^\s"']+/g) || [];
|
|
7561
|
+
if (pieces.join("").replace(/\s/g, "") !== command.replace(/\s/g, "")) return false;
|
|
7562
|
+
const tokens = pieces.map((piece) => piece.replace(/^["']|["']$/g, ""));
|
|
7563
|
+
if (!/^gcloud(?:\.cmd|\.exe)?$/i.test(tokens.shift() || "") || tokens.shift() !== "storage" || tokens.shift() !== "cat" || tokens.length > 32) return false;
|
|
7564
|
+
let objects = 0;
|
|
7565
|
+
for (let i2 = 0; i2 < tokens.length; i2++) {
|
|
7566
|
+
const token = tokens[i2];
|
|
7567
|
+
if (/^gs:\/\/[a-z0-9][a-z0-9._-]*\/[A-Za-z0-9._~/*?-]+$/.test(token)) {
|
|
7568
|
+
objects++;
|
|
7569
|
+
continue;
|
|
7570
|
+
}
|
|
7571
|
+
if (token === "--display-url" || token === "-d") continue;
|
|
7572
|
+
if ((token === "--range" || token === "-r") && /^(?:\d+-\d*|-\d+)$/.test(tokens[i2 + 1] || "")) {
|
|
7573
|
+
i2++;
|
|
7574
|
+
continue;
|
|
7575
|
+
}
|
|
7576
|
+
if (/^--range=(?:\d+-\d*|-\d+)$/.test(token)) continue;
|
|
7577
|
+
return false;
|
|
7578
|
+
}
|
|
7579
|
+
return objects > 0;
|
|
7580
|
+
}
|
|
7464
7581
|
function provenCompoundDownload(command, detectedUrl) {
|
|
7465
7582
|
if (command.includes(detectedUrl) && provenPowerShellReadSequence(command)) return true;
|
|
7466
7583
|
const segments = splitShellSegments(command).filter((segment) => /\b(?:https?|s3|gs|ftp|sftp|smb):\/\//i.test(segment));
|
|
@@ -7470,8 +7587,9 @@ function provenCompoundDownload(command, detectedUrl) {
|
|
|
7470
7587
|
if (statusRead) segment = statusRead[1];
|
|
7471
7588
|
if (/[$`{}()<>;&]/.test(segment)) return false;
|
|
7472
7589
|
const pipeline = splitShellSegments(segment, true);
|
|
7473
|
-
if (pipeline.slice(1).some((part) =>
|
|
7590
|
+
if (pipeline.slice(1).some((part) => !isPowerShellReadFormatter(part))) return false;
|
|
7474
7591
|
const raw = pipeline[0];
|
|
7592
|
+
if (isGcloudStorageCatRead(raw)) return true;
|
|
7475
7593
|
const pieces = raw.match(/"[^"\r\n]*"|'[^'\r\n]*'|[^\s"']+/g) || [];
|
|
7476
7594
|
if (pieces.join("").replace(/\s/g, "") !== raw.replace(/\s/g, "")) return false;
|
|
7477
7595
|
const tokens = pieces.map((piece) => piece.replace(/^["']|["']$/g, ""));
|
|
@@ -7515,13 +7633,41 @@ function provenCompoundDownload(command, detectedUrl) {
|
|
|
7515
7633
|
return urls === 1;
|
|
7516
7634
|
});
|
|
7517
7635
|
}
|
|
7636
|
+
function isPowerShellClockDisplay(value) {
|
|
7637
|
+
return /^\(\s*Get-Date\s*\)\s*\.\s*(?:ToUniversalTime\s*\(\s*\)\s*\.\s*)?ToString\s*\(\s*(?:'[^'\r\n]{0,80}'|"[^"$`\r\n]{0,80}")\s*\)$/i.test(value);
|
|
7638
|
+
}
|
|
7639
|
+
function isPowerShellResponseProjection(segment, responses) {
|
|
7640
|
+
const object = /^\[pscustomobject\]\s*@\{([^{}]+)\}\s*(?:\|\s*(.+))?$/i.exec(segment);
|
|
7641
|
+
if (!object) return false;
|
|
7642
|
+
if (object[2] && object[2].split("|").some((part) => !isPowerShellReadFormatter(part.trim()))) return false;
|
|
7643
|
+
const fields = object[1].trim().replace(/;\s*$/, "").split(";");
|
|
7644
|
+
if (fields.length > 32) return false;
|
|
7645
|
+
const labels = /* @__PURE__ */ new Set();
|
|
7646
|
+
return fields.every((field) => {
|
|
7647
|
+
const entry = /^\s*([A-Za-z_][\w]*)\s*=\s*(.+?)\s*$/s.exec(field);
|
|
7648
|
+
if (!entry) return false;
|
|
7649
|
+
const label = entry[1].toLowerCase();
|
|
7650
|
+
if (labels.has(label)) return false;
|
|
7651
|
+
labels.add(label);
|
|
7652
|
+
if (isPowerShellClockDisplay(entry[2])) return true;
|
|
7653
|
+
const member = /^(\[int\]\s*)?\$([A-Za-z_][\w]*)\.(StatusCode|StatusDescription|RawContentLength|Content)$/i.exec(entry[2]);
|
|
7654
|
+
return Boolean(member && responses.has(member[2].toLowerCase()) && (!member[1] || /^(?:StatusCode|RawContentLength)$/i.test(member[3])));
|
|
7655
|
+
});
|
|
7656
|
+
}
|
|
7518
7657
|
function provenPowerShellReadSequence(command) {
|
|
7519
|
-
if (command.length > 65536 || !/(?:^|[;&\n])\s
|
|
7658
|
+
if (command.length > 65536 || !/(?:^|[;&\n])\s*(?:\$[A-Za-z_][\w]*\s*=\s*(?:Invoke-RestMethod|Invoke-WebRequest|irm|iwr)\s|(?:\$[A-Za-z_][\w]*\s*=\s*)?gcloud(?:\.cmd|\.exe)?\s+storage\s+cat\s)/i.test(command)) return false;
|
|
7520
7659
|
const segments = splitShellSegments(command);
|
|
7521
|
-
if (segments.length > 32 ||
|
|
7660
|
+
if (segments.length > 32 || /`/.test(command)) return false;
|
|
7522
7661
|
const responses = /* @__PURE__ */ new Set();
|
|
7523
7662
|
let requests = 0;
|
|
7524
|
-
for (let
|
|
7663
|
+
for (let index = 0; index < segments.length; index++) {
|
|
7664
|
+
let segment = segments[index];
|
|
7665
|
+
if (/^\[pscustomobject\]\s*@\{/i.test(segment)) {
|
|
7666
|
+
while (!segment.includes("}") && index + 1 < segments.length) segment += ";" + segments[++index];
|
|
7667
|
+
if (!isPowerShellResponseProjection(segment, responses)) return false;
|
|
7668
|
+
continue;
|
|
7669
|
+
}
|
|
7670
|
+
if (/[{}]/.test(maskQuotedSpans(segment))) return false;
|
|
7525
7671
|
let binding;
|
|
7526
7672
|
const assignment = /^\$([A-Za-z_][\w]*)\s*=\s*(.+)$/s.exec(segment);
|
|
7527
7673
|
if (assignment) {
|
|
@@ -7533,15 +7679,18 @@ function provenPowerShellReadSequence(command) {
|
|
|
7533
7679
|
if (property) segment = property[1];
|
|
7534
7680
|
const pipeline = splitShellSegments(segment, true);
|
|
7535
7681
|
const lead = pipeline.shift() || "";
|
|
7536
|
-
if (pipeline.some((part) =>
|
|
7537
|
-
if (
|
|
7682
|
+
if (pipeline.some((part) => !isPowerShellReadFormatter(part))) return false;
|
|
7683
|
+
if (isGcloudStorageCatRead(lead)) {
|
|
7684
|
+
requests++;
|
|
7685
|
+
} else if (/^(?:Invoke-RestMethod|Invoke-WebRequest|irm|iwr)\s/i.test(lead)) {
|
|
7538
7686
|
const url = lead.match(/https?:\/\/[^\s"'<>]+/i)?.[0];
|
|
7539
7687
|
if (!url || !provenCompoundDownload(lead, url)) return false;
|
|
7540
7688
|
requests++;
|
|
7541
|
-
} else if (/^\$[A-Za-z_][\w]*$/.test(lead)) {
|
|
7542
|
-
if (
|
|
7689
|
+
} else if (/^\$[A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)*$/.test(lead)) {
|
|
7690
|
+
if (!responses.has(lead.slice(1).split(".")[0].toLowerCase())) return false;
|
|
7543
7691
|
} else {
|
|
7544
7692
|
if (binding) return false;
|
|
7693
|
+
if (/^gh\s+(?:run|pr)\s+view\s+\d+\s+--json\s+(?:[A-Za-z][A-Za-z,]*|'[A-Za-z][A-Za-z, ]*'|"[A-Za-z][A-Za-z, ]*")(?:\s+--jq\s+'[^'$`\r\n]{1,2000}')?$/i.test(lead) || /^npm\s+view\s+(?:@[\w.-]+\/)?[\w.-]+\s+version$/i.test(lead) || /^Get-ScheduledTask\s+-TaskName\s+(?:[\w.-]+|'[^'$`\r\n]+'|"[^"$`\r\n]+")$/i.test(lead)) continue;
|
|
7545
7694
|
if (pipeline.length === 0 && /^(?:git\s+status(?:\s+--short)?|cd\s+(?:[A-Za-z]:[\\/][\w./\\-]+|'[A-Za-z]:[\\/][^'$`\r\n]+'))$/i.test(lead)) continue;
|
|
7546
7695
|
const file = /^Get-Content\s+(?:'([^'\r\n]+)'|"([^"$`\r\n]+)")((?:\s+-(?:Raw|Tail\s+\d+|TotalCount\s+\d+))*)$/i.exec(lead);
|
|
7547
7696
|
if (!file || !/^[A-Za-z]:[\\/]/.test(file[1] || file[2]) || /[?*]/.test(file[1] || file[2])) return false;
|
|
@@ -7723,6 +7872,35 @@ function evaluateActionPolicies(policies, toolName, operation, context = {}, dec
|
|
|
7723
7872
|
return worstResult;
|
|
7724
7873
|
}
|
|
7725
7874
|
function inferToolContext(toolName, args) {
|
|
7875
|
+
const detectorArgs = {};
|
|
7876
|
+
const untrustedInput = /* @__PURE__ */ Object.create(null);
|
|
7877
|
+
for (const [key, value] of Object.entries(args)) {
|
|
7878
|
+
if (reservedInputFact(key) || LEGACY_TRUST_LABEL_ARGS.has(key)) untrustedInput[key] = value;
|
|
7879
|
+
else detectorArgs[key] = value;
|
|
7880
|
+
}
|
|
7881
|
+
if (Object.keys(untrustedInput).length) {
|
|
7882
|
+
let carrier = "__fcd_untrustedInput";
|
|
7883
|
+
while (Object.hasOwn(detectorArgs, carrier)) carrier += "_";
|
|
7884
|
+
detectorArgs[carrier] = untrustedInput;
|
|
7885
|
+
}
|
|
7886
|
+
const updated = (0, import_detectorRuntime.updatedDetectorFacts)(toolName, detectorArgs);
|
|
7887
|
+
if (!updated) return inferBundledToolContext(toolName, args);
|
|
7888
|
+
const context = { ...updated.context };
|
|
7889
|
+
for (const key of RESERVED_INPUT_FACTS) delete context[key];
|
|
7890
|
+
const argsText = stringifyArgs(args);
|
|
7891
|
+
context["toolArgs.bytes"] = String(Buffer.byteLength(argsText, "utf8"));
|
|
7892
|
+
for (const [key, value] of Object.entries(args)) {
|
|
7893
|
+
if (!["string", "number", "boolean"].includes(typeof value)) continue;
|
|
7894
|
+
context[`args.${key}`] = String(value);
|
|
7895
|
+
if (!reservedInputFact(key) && LEGACY_TRUST_LABEL_ARGS.has(key) && !Object.hasOwn(context, key)) context[key] = String(value);
|
|
7896
|
+
}
|
|
7897
|
+
if (!Object.hasOwn(context, "request.containsSecret") && recordHttpRequestFacts(context, args, extractUrl(args, argsText))) {
|
|
7898
|
+
context["destination.type"] = context["request.destination.type"];
|
|
7899
|
+
context["destination.domain"] = context["request.destination.domain"];
|
|
7900
|
+
}
|
|
7901
|
+
return { operation: updated.operation, context };
|
|
7902
|
+
}
|
|
7903
|
+
function inferBundledToolContext(toolName, args) {
|
|
7726
7904
|
const context = {};
|
|
7727
7905
|
let operation = toolName;
|
|
7728
7906
|
const toolNameLower = toolName.toLowerCase();
|
|
@@ -7734,19 +7912,13 @@ function inferToolContext(toolName, args) {
|
|
|
7734
7912
|
Number.isFinite(retryCount) ? retryCount : 0,
|
|
7735
7913
|
Number.isFinite(fanoutCount) ? fanoutCount : 0
|
|
7736
7914
|
);
|
|
7737
|
-
context
|
|
7738
|
-
const secretKind = detectSecretKind(argsText);
|
|
7739
|
-
context["toolArgs.containsSecret"] = secretKind !== "none" ? "true" : "false";
|
|
7740
|
-
context["toolArgs.secretKind"] = secretKind;
|
|
7741
|
-
context["toolArgs.containsPii"] = hasPiiLikeValue(argsText) ? "true" : "false";
|
|
7915
|
+
Object.assign(context, inputContentFacts(argsText));
|
|
7742
7916
|
context["retry.count"] = String(burstCount);
|
|
7743
7917
|
context["fanout.count"] = String(burstCount);
|
|
7744
7918
|
for (const [key, val] of Object.entries(args)) {
|
|
7745
|
-
if (typeof val
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
context[key] = String(val);
|
|
7749
|
-
}
|
|
7919
|
+
if (typeof val !== "string" && typeof val !== "number" && typeof val !== "boolean") continue;
|
|
7920
|
+
context[`args.${key}`] = String(val);
|
|
7921
|
+
if (!reservedInputFact(key)) context[key] = String(val);
|
|
7750
7922
|
}
|
|
7751
7923
|
const nameWords = toolNameLower.replace(/[_\-.]/g, " ");
|
|
7752
7924
|
const isSearchTool = /\b(search|grep|find|lookup|locate)\b/.test(nameWords) && !/\b(delete|remove|drop|write|update|insert|replace|create)\b/.test(nameWords);
|
|
@@ -7779,7 +7951,7 @@ function inferToolContext(toolName, args) {
|
|
|
7779
7951
|
else if (writeOps.some((op) => toolNameLower.includes(op))) operation = "write";
|
|
7780
7952
|
else if (deleteOps.some((op) => toolNameLower.includes(op))) operation = "delete";
|
|
7781
7953
|
}
|
|
7782
|
-
const methodArg =
|
|
7954
|
+
const methodArg = typeof args.method === "string" ? args.method.toUpperCase() : "";
|
|
7783
7955
|
if (["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"].includes(methodArg)) {
|
|
7784
7956
|
operation = methodArg;
|
|
7785
7957
|
if (args.url) context.url = args.url;
|
|
@@ -7794,20 +7966,19 @@ function inferToolContext(toolName, args) {
|
|
|
7794
7966
|
const inbound = operation === "read" || ["GET", "HEAD", "OPTIONS", "SELECT", "SHOW", "DESCRIBE", "EXPLAIN"].includes(operation) || isCodeExecTool && !hasSecretLikeValue(argsText) && provenCompoundDownload(commandText, detectedUrl) || provenReadUrls.includes(detectedUrl) || operation === toolName && /\b(fetch|get|read|search|list|lookup|browse|navigate|download|clone|pull|view|open|crawl|scrape)\b/.test(nameWords) && !/\b(post|put|send|upload|push|write|submit|publish|export|create|update|delete|patch)\b/.test(nameWords);
|
|
7795
7967
|
context["destination.domain"] = parsed.hostname;
|
|
7796
7968
|
context["url.risk"] = hostType;
|
|
7969
|
+
const requestHasSecret = recordHttpRequestFacts(context, args, detectedUrl);
|
|
7797
7970
|
if (inbound) {
|
|
7798
7971
|
context["source.domain"] = parsed.hostname;
|
|
7799
7972
|
context["source.type"] = hostType;
|
|
7800
|
-
}
|
|
7973
|
+
}
|
|
7974
|
+
if (!inbound || requestHasSecret) {
|
|
7801
7975
|
context["destination.type"] = hostType;
|
|
7802
7976
|
}
|
|
7803
7977
|
} catch {
|
|
7804
7978
|
context["url.risk"] = "invalid";
|
|
7805
7979
|
}
|
|
7806
7980
|
}
|
|
7807
|
-
if (typeof args.destinationType === "string") context["destination.type"] = args.destinationType;
|
|
7808
7981
|
if (typeof args.destination === "string") context.destination = args.destination;
|
|
7809
|
-
if (typeof args.domain === "string") context["destination.domain"] = args.domain;
|
|
7810
|
-
if (typeof args.toolStatus === "string") context["tool.status"] = args.toolStatus;
|
|
7811
7982
|
if (args.to || args.recipient || args.email) {
|
|
7812
7983
|
context.to = args.to || args.recipient || args.email;
|
|
7813
7984
|
context["destination.type"] = context["destination.type"] || "external";
|
|
@@ -7830,6 +8001,77 @@ function inferToolContext(toolName, args) {
|
|
|
7830
8001
|
}
|
|
7831
8002
|
return { operation, context };
|
|
7832
8003
|
}
|
|
8004
|
+
var RESERVED_INPUT_FACTS = /* @__PURE__ */ new Set([
|
|
8005
|
+
"__proto__",
|
|
8006
|
+
"constructor",
|
|
8007
|
+
"prototype",
|
|
8008
|
+
"verdict",
|
|
8009
|
+
"decision",
|
|
8010
|
+
"approval",
|
|
8011
|
+
"toolName",
|
|
8012
|
+
"agentName",
|
|
8013
|
+
"agentClient",
|
|
8014
|
+
"developerName",
|
|
8015
|
+
"machineName",
|
|
8016
|
+
"machineKind",
|
|
8017
|
+
"machineId",
|
|
8018
|
+
"humanPresent",
|
|
8019
|
+
"organizationId",
|
|
8020
|
+
"tenantId",
|
|
8021
|
+
"shieldId",
|
|
8022
|
+
"userId",
|
|
8023
|
+
"ipAddress",
|
|
8024
|
+
"gateway",
|
|
8025
|
+
"mcpServer"
|
|
8026
|
+
]);
|
|
8027
|
+
var RESERVED_FACT_NAMESPACES = /* @__PURE__ */ new Set([
|
|
8028
|
+
"args",
|
|
8029
|
+
"toolArgs",
|
|
8030
|
+
"tool",
|
|
8031
|
+
"agent",
|
|
8032
|
+
"action",
|
|
8033
|
+
"operation",
|
|
8034
|
+
"url",
|
|
8035
|
+
"destination",
|
|
8036
|
+
"source",
|
|
8037
|
+
"request",
|
|
8038
|
+
"retry",
|
|
8039
|
+
"fanout",
|
|
8040
|
+
"runtime",
|
|
8041
|
+
"machine",
|
|
8042
|
+
"session",
|
|
8043
|
+
"policy",
|
|
8044
|
+
"tenant",
|
|
8045
|
+
"organization",
|
|
8046
|
+
"role",
|
|
8047
|
+
"roles"
|
|
8048
|
+
]);
|
|
8049
|
+
function reservedInputFact(key) {
|
|
8050
|
+
return RESERVED_INPUT_FACTS.has(key) || key.includes(".") && RESERVED_FACT_NAMESPACES.has(key.split(".")[0]);
|
|
8051
|
+
}
|
|
8052
|
+
var LEGACY_TRUST_LABEL_ARGS = /* @__PURE__ */ new Set(["destinationType", "domain", "toolStatus"]);
|
|
8053
|
+
function inputContentFacts(argsText) {
|
|
8054
|
+
const secretKind = detectSecretKind(argsText);
|
|
8055
|
+
return {
|
|
8056
|
+
"toolArgs.bytes": String(Buffer.byteLength(argsText, "utf8")),
|
|
8057
|
+
"toolArgs.containsSecret": secretKind !== "none" ? "true" : "false",
|
|
8058
|
+
"toolArgs.secretKind": secretKind,
|
|
8059
|
+
"toolArgs.containsPii": hasPiiLikeValue(argsText) ? "true" : "false"
|
|
8060
|
+
};
|
|
8061
|
+
}
|
|
8062
|
+
function recordHttpRequestFacts(context, args, detectedUrl) {
|
|
8063
|
+
try {
|
|
8064
|
+
const parsed = new URL(detectedUrl);
|
|
8065
|
+
if (!["http:", "https:"].includes(parsed.protocol)) return false;
|
|
8066
|
+
const containsSecret = hasSecretLikeValue(stringifyArgs({ url: detectedUrl, headers: args.headers, body: args.body, data: args.data }));
|
|
8067
|
+
context["request.destination.domain"] = parsed.hostname;
|
|
8068
|
+
context["request.destination.type"] = isInternalHost(parsed.hostname) ? "internal" : "external";
|
|
8069
|
+
context["request.containsSecret"] = containsSecret ? "true" : "false";
|
|
8070
|
+
return containsSecret;
|
|
8071
|
+
} catch {
|
|
8072
|
+
return false;
|
|
8073
|
+
}
|
|
8074
|
+
}
|
|
7833
8075
|
function inferInventoryToolOperations(toolName, toolActions = []) {
|
|
7834
8076
|
const ops = /* @__PURE__ */ new Set([toolName, toolName.toUpperCase(), ...toolActions]);
|
|
7835
8077
|
const upper = toolName.toUpperCase();
|
|
@@ -7863,6 +8105,7 @@ function inventoryToolMatchesActionPolicy(toolName, toolActions, policies) {
|
|
|
7863
8105
|
detectSecretKind,
|
|
7864
8106
|
effectiveApprovalScope,
|
|
7865
8107
|
evaluateActionPolicies,
|
|
8108
|
+
inferBundledToolContext,
|
|
7866
8109
|
inferToolContext,
|
|
7867
8110
|
inventoryToolMatchesActionPolicy,
|
|
7868
8111
|
isLocalFileContentAction,
|
package/dist/agentTerminal.d.ts
CHANGED
|
@@ -14,9 +14,7 @@ export declare const AGENT_TERMINAL_ASK_BLOCKED: string;
|
|
|
14
14
|
/** Shown when the IDE prompt already answered for this exact command. */
|
|
15
15
|
export declare const AGENT_TERMINAL_IDE_CONFIRMED = "allowed through the IDE permission gate; confirmation is recorded by the IDE post-execution hook.";
|
|
16
16
|
/** Pending-marker key the IDE hook wrote for this shell command (see devConfirm.pendingKey). */
|
|
17
|
-
export declare function ideShellPendingKey(command: string): string;
|
|
18
|
-
/** Remembered-confirmation key for a Local Safety ask rule on the shell tool (see devConfirm rememberKey). */
|
|
19
|
-
export declare function ideLocalSafetyRememberKey(ruleId: string): string;
|
|
17
|
+
export declare function ideShellPendingKey(command: string, cwd?: string): string;
|
|
20
18
|
/** Maximum lifetime of an exact-command IDE permission handoff (not consent). */
|
|
21
19
|
export declare const IDE_CONFIRMATION_WINDOW_MS: number;
|
|
22
20
|
/**
|