fullcourtdefense-cli 1.34.25 → 1.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actionPolicyEngine.d.ts +13 -0
- package/dist/actionPolicyEngine.js +393 -18
- package/dist/approvalPrompt.d.ts +4 -0
- package/dist/approvalPrompt.js +59 -12
- package/dist/askDialog.d.ts +14 -1
- package/dist/askDialog.js +9 -7
- package/dist/commands/deterministicGuard.js +10 -3
- package/dist/commands/ephemeralStack.d.ts +36 -0
- package/dist/commands/ephemeralStack.js +126 -1
- package/dist/commands/grants.d.ts +23 -0
- package/dist/commands/grants.js +79 -0
- package/dist/commands/hook.js +21 -8
- package/dist/commands/httpGateway.d.ts +66 -0
- package/dist/commands/httpGateway.js +273 -0
- package/dist/commands/httpGatewayCommand.d.ts +87 -0
- package/dist/commands/httpGatewayCommand.js +450 -0
- package/dist/commands/mcpGateway.d.ts +95 -0
- package/dist/commands/mcpGateway.js +40 -18
- package/dist/commands/workloadProtect.d.ts +8 -0
- package/dist/commands/workloadProtect.js +8 -0
- package/dist/detectorBaseline.d.ts +2 -2
- package/dist/detectorBaseline.js +2 -2
- package/dist/devConfirm.d.ts +102 -5
- package/dist/devConfirm.js +150 -21
- package/dist/httpEgress.d.ts +111 -0
- package/dist/httpEgress.js +240 -0
- package/dist/index.js +54 -0
- package/dist/machineActionVerify.d.ts +7 -0
- package/dist/machineActionVerify.js +7 -1
- package/dist/runtimeConfig.d.ts +22 -0
- package/dist/runtimeConfig.js +39 -0
- package/dist/telemetry.d.ts +24 -1
- package/dist/telemetry.js +11 -5
- package/dist/version.json +1 -1
- package/package.json +5 -1
|
@@ -29,13 +29,26 @@ export interface ActionPolicyApproval {
|
|
|
29
29
|
scope?: ActionPolicyApprovalScope;
|
|
30
30
|
remember?: ActionPolicyApprovalRemember;
|
|
31
31
|
onTimeout?: ActionPolicyApprovalOnTimeout;
|
|
32
|
+
/**
|
|
33
|
+
* Caps on a developer's "Always allow" under this rule: lifetime in days
|
|
34
|
+
* (1–30) and uses before the dialog returns (1–10000). Absent = the built-in
|
|
35
|
+
* 30-day lifetime, unlimited uses. The org ceiling delivered with the bundle
|
|
36
|
+
* can only tighten these, never widen them.
|
|
37
|
+
*/
|
|
38
|
+
maxDays?: number;
|
|
39
|
+
maxUses?: number;
|
|
32
40
|
}
|
|
33
41
|
/** Fully-resolved approval options (every field present) attached to a `require_approval` result. */
|
|
34
42
|
export interface ResolvedActionPolicyApproval {
|
|
35
43
|
scope: ActionPolicyApprovalScope;
|
|
36
44
|
remember: ActionPolicyApprovalRemember;
|
|
37
45
|
onTimeout: ActionPolicyApprovalOnTimeout;
|
|
46
|
+
/** "Always allow" caps (see `ActionPolicyApproval`); undefined = built-in default. */
|
|
47
|
+
maxDays?: number;
|
|
48
|
+
maxUses?: number;
|
|
38
49
|
}
|
|
50
|
+
export declare const ACTION_POLICY_GRANT_MAX_DAYS = 30;
|
|
51
|
+
export declare const ACTION_POLICY_GRANT_MAX_USES = 10000;
|
|
39
52
|
export interface ActionPolicyRule {
|
|
40
53
|
/** Explicit, narrowly scoped permission to relax named role actions only. */
|
|
41
54
|
roleException?: {
|
|
@@ -20,6 +20,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/actionPolicyEngine.ts
|
|
21
21
|
var actionPolicyEngine_exports = {};
|
|
22
22
|
__export(actionPolicyEngine_exports, {
|
|
23
|
+
ACTION_POLICY_GRANT_MAX_DAYS: () => ACTION_POLICY_GRANT_MAX_DAYS,
|
|
24
|
+
ACTION_POLICY_GRANT_MAX_USES: () => ACTION_POLICY_GRANT_MAX_USES,
|
|
23
25
|
CONTENT_FACT_FIELDS: () => CONTENT_FACT_FIELDS,
|
|
24
26
|
DEFAULT_ACTION_POLICY_APPROVAL: () => DEFAULT_ACTION_POLICY_APPROVAL,
|
|
25
27
|
MACHINE_KINDS: () => MACHINE_KINDS,
|
|
@@ -6237,6 +6239,18 @@ function provenPowerShellQueryUrls(command) {
|
|
|
6237
6239
|
return [];
|
|
6238
6240
|
}
|
|
6239
6241
|
}
|
|
6242
|
+
var ACTION_POLICY_GRANT_MAX_DAYS = 30;
|
|
6243
|
+
var ACTION_POLICY_GRANT_MAX_USES = 1e4;
|
|
6244
|
+
function approvalCap(value, limit) {
|
|
6245
|
+
const n = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : NaN;
|
|
6246
|
+
if (!Number.isFinite(n) || n < 1) return void 0;
|
|
6247
|
+
return Math.min(limit, Math.floor(n));
|
|
6248
|
+
}
|
|
6249
|
+
function minCap(a, b) {
|
|
6250
|
+
if (a === void 0) return b;
|
|
6251
|
+
if (b === void 0) return a;
|
|
6252
|
+
return Math.min(a, b);
|
|
6253
|
+
}
|
|
6240
6254
|
function approvalExceptionError(rule) {
|
|
6241
6255
|
const roleError = roleExceptionError(rule);
|
|
6242
6256
|
if (roleError) return roleError;
|
|
@@ -6314,17 +6328,25 @@ function resolveApprovalOptions(approval) {
|
|
|
6314
6328
|
const scope = approval?.scope;
|
|
6315
6329
|
const remember = approval?.remember;
|
|
6316
6330
|
const onTimeout = approval?.onTimeout;
|
|
6331
|
+
const maxDays = approvalCap(approval?.maxDays, ACTION_POLICY_GRANT_MAX_DAYS);
|
|
6332
|
+
const maxUses = approvalCap(approval?.maxUses, ACTION_POLICY_GRANT_MAX_USES);
|
|
6317
6333
|
return {
|
|
6318
6334
|
scope: scope === "developer" || scope === "org" ? scope : DEFAULT_ACTION_POLICY_APPROVAL.scope,
|
|
6319
6335
|
remember: remember === "session" || remember === "1h" || remember === "none" ? remember : DEFAULT_ACTION_POLICY_APPROVAL.remember,
|
|
6320
|
-
onTimeout: onTimeout === "org" || onTimeout === "block" ? onTimeout : DEFAULT_ACTION_POLICY_APPROVAL.onTimeout
|
|
6336
|
+
onTimeout: onTimeout === "org" || onTimeout === "block" ? onTimeout : DEFAULT_ACTION_POLICY_APPROVAL.onTimeout,
|
|
6337
|
+
...maxDays !== void 0 ? { maxDays } : {},
|
|
6338
|
+
...maxUses !== void 0 ? { maxUses } : {}
|
|
6321
6339
|
};
|
|
6322
6340
|
}
|
|
6323
6341
|
function mergeApprovalOptions(a, b) {
|
|
6342
|
+
const maxDays = minCap(a.maxDays, b.maxDays);
|
|
6343
|
+
const maxUses = minCap(a.maxUses, b.maxUses);
|
|
6324
6344
|
return {
|
|
6325
6345
|
scope: APPROVAL_SCOPE_RANK[b.scope] > APPROVAL_SCOPE_RANK[a.scope] ? b.scope : a.scope,
|
|
6326
6346
|
remember: APPROVAL_REMEMBER_RANK[b.remember] > APPROVAL_REMEMBER_RANK[a.remember] ? b.remember : a.remember,
|
|
6327
|
-
onTimeout: APPROVAL_ON_TIMEOUT_RANK[b.onTimeout] > APPROVAL_ON_TIMEOUT_RANK[a.onTimeout] ? b.onTimeout : a.onTimeout
|
|
6347
|
+
onTimeout: APPROVAL_ON_TIMEOUT_RANK[b.onTimeout] > APPROVAL_ON_TIMEOUT_RANK[a.onTimeout] ? b.onTimeout : a.onTimeout,
|
|
6348
|
+
...maxDays !== void 0 ? { maxDays } : {},
|
|
6349
|
+
...maxUses !== void 0 ? { maxUses } : {}
|
|
6328
6350
|
};
|
|
6329
6351
|
}
|
|
6330
6352
|
function effectiveApprovalScope(approval, presence) {
|
|
@@ -6790,14 +6812,24 @@ var COMMAND_LINE_FIELDS = /* @__PURE__ */ new Set(["command", "cmd", "commandLin
|
|
|
6790
6812
|
var CODE_EXEC_TOOL_NAME_RE = /\b(node ?repl|repl|shell|exec|execute|terminal|command|cmd|run ?code|bash|zsh|sh|powershell|pwsh)\b/i;
|
|
6791
6813
|
function shellVerbWords(command) {
|
|
6792
6814
|
let lead = "";
|
|
6815
|
+
let first = true;
|
|
6816
|
+
let scriptArgs = false;
|
|
6793
6817
|
return shellCommandWords(command).split(/\s+/).filter(Boolean).map((token) => {
|
|
6794
6818
|
if (token === ";") {
|
|
6795
6819
|
lead = "";
|
|
6820
|
+
first = true;
|
|
6821
|
+
scriptArgs = false;
|
|
6796
6822
|
return token;
|
|
6797
6823
|
}
|
|
6824
|
+
if (scriptArgs) return "";
|
|
6798
6825
|
const flag = /^(-{1,2})([A-Za-z][A-Za-z0-9_-]*)(?:=.*)?$/.exec(token);
|
|
6799
6826
|
const word = flag ? flag[2] : /^\.?[A-Za-z][A-Za-z0-9_-]*$/.test(token) ? token : "";
|
|
6800
|
-
|
|
6827
|
+
const leading = first && !flag;
|
|
6828
|
+
if (leading) first = false;
|
|
6829
|
+
if (!word) {
|
|
6830
|
+
if (leading ? isScriptPath(token, true) : !flag && SCRIPT_RUNNER_LEADS.has(lead) && isScriptPath(token, false)) scriptArgs = true;
|
|
6831
|
+
return "";
|
|
6832
|
+
}
|
|
6801
6833
|
if (!flag && !lead) lead = word.toLowerCase();
|
|
6802
6834
|
if (flag) {
|
|
6803
6835
|
if (VCS_PROGRAMS.has(lead)) return "";
|
|
@@ -6812,6 +6844,50 @@ function shellVerbWords(command) {
|
|
|
6812
6844
|
}).filter(Boolean).join(" ");
|
|
6813
6845
|
}
|
|
6814
6846
|
var VCS_PROGRAMS = /* @__PURE__ */ new Set(["git", "gh", "hg", "svn", "glab"]);
|
|
6847
|
+
var SCRIPT_RUNNER_LEADS = /* @__PURE__ */ new Set([
|
|
6848
|
+
"bash",
|
|
6849
|
+
"sh",
|
|
6850
|
+
"zsh",
|
|
6851
|
+
"dash",
|
|
6852
|
+
"ksh",
|
|
6853
|
+
"fish",
|
|
6854
|
+
"cmd",
|
|
6855
|
+
"powershell",
|
|
6856
|
+
"pwsh",
|
|
6857
|
+
"sudo",
|
|
6858
|
+
"doas",
|
|
6859
|
+
"env",
|
|
6860
|
+
"nohup",
|
|
6861
|
+
"time",
|
|
6862
|
+
"timeout",
|
|
6863
|
+
"watch",
|
|
6864
|
+
"ssh",
|
|
6865
|
+
"xargs",
|
|
6866
|
+
"node",
|
|
6867
|
+
"nodejs",
|
|
6868
|
+
"deno",
|
|
6869
|
+
"bun",
|
|
6870
|
+
"python",
|
|
6871
|
+
"python3",
|
|
6872
|
+
"python2",
|
|
6873
|
+
"py",
|
|
6874
|
+
"perl",
|
|
6875
|
+
"ruby",
|
|
6876
|
+
"php",
|
|
6877
|
+
"lua",
|
|
6878
|
+
"rscript",
|
|
6879
|
+
"julia",
|
|
6880
|
+
"tsx",
|
|
6881
|
+
"ts-node",
|
|
6882
|
+
"npx"
|
|
6883
|
+
]);
|
|
6884
|
+
var SCRIPT_EXTENSION_RE = /\.(?:ps1|psm1|sh|bash|zsh|ksh|fish|py|pyw|js|mjs|cjs|ts|mts|cts|rb|pl|php|lua|r|jl|cmd|bat)$/i;
|
|
6885
|
+
function isScriptPath(token, leading) {
|
|
6886
|
+
const t = token.replace(/^["']|["']$/g, "");
|
|
6887
|
+
if (!t || /^[$%<>|&]/.test(t)) return false;
|
|
6888
|
+
if (SCRIPT_EXTENSION_RE.test(t)) return true;
|
|
6889
|
+
return leading ? /^\.{1,2}[\\/]/.test(t) : /[\\/]/.test(t);
|
|
6890
|
+
}
|
|
6815
6891
|
var DOT_COMMAND_PROGRAMS = /* @__PURE__ */ new Set(["sqlite3", "sqlite", "litecli", "duckdb"]);
|
|
6816
6892
|
function stringifyArgs(args) {
|
|
6817
6893
|
try {
|
|
@@ -6866,13 +6942,14 @@ function isHighEntropySecretToken(token, allowWordSlug = false) {
|
|
|
6866
6942
|
}
|
|
6867
6943
|
var SECRET_NAME = String.raw`(?:api[_-]?key|apikey|access[_-]?token|auth[_-]?token|refresh[_-]?token|bearer[_-]?token|client[_-]?secret|secret[_-]?key|secret|password|passwd|pwd|credentials?|private[_-]?key|token)`;
|
|
6868
6944
|
var SECRET_ASSIGNMENT_RE = new RegExp(String.raw`(?:^|[^\w])[\w.-]*?${SECRET_NAME}[\\"']*\s*(?:=>|[:=])\s*[\\"']*([^\s\\"',;&|)}\]<>]{8,})`, "gi");
|
|
6869
|
-
var SECRET_FLAG_RE =
|
|
6870
|
-
var SECRET_PLACEHOLDER_RE = /^(?:<.*>|\$\{?[A-Za-z_][\w.]*\}?|%[A-Za-z_]+%|\{\{.*\}\}|\[.*\]|\*{3,}|x{4,}|X{4,}|\.{3,}|-{3,}|_{3,}|(?:your|my|our|the|a|an
|
|
6945
|
+
var SECRET_FLAG_RE = /(?:^|[\s"'`;(\[{,])--?(?:password|passwd|pwd|token|api-?key|secret|client-secret|access-token)(?:=|\s+)[\\"']*([^\s\\"',;&|)}\]<>-][^\s\\"',;&|)}\]<>]{7,})/gi;
|
|
6946
|
+
var SECRET_PLACEHOLDER_RE = /^(?:<.*>|\$\{?[A-Za-z_][\w.]*\}?|%[A-Za-z_]+%|\{\{.*\}\}|\[.*\]|\*{3,}|x{4,}|X{4,}|\.{3,}|-{3,}|_{3,}|(?:your|my|our|the|a|an)(?:[-_.][\w.-]*)?|(?:some|example|sample|dummy|fake|test|mock|placeholder|redacted|masked|hidden|changeme|change_me|replace_?me|todo|tbd|fixme|none|null|nil|undefined|true|false|required|optional|omitted|removed|unset|empty|secret|password|token|xxx+|abc+|123+)[\w.-]*)$/i;
|
|
6871
6947
|
var SECRET_CODE_EXPR_RE = /^(?:[A-Za-z_$][\w$]*(?:[.\[][^\s=]*)+|[A-Za-z_$][\w$]*\(.*\)?|process\.env\.\w+|os\.environ.*|env(?:iron)?\.\w+|new\s+\w+.*|await\s+.*|require\(.*)$/;
|
|
6872
6948
|
function looksLikeSecretValue(raw) {
|
|
6873
6949
|
const value = raw.replace(/[\\"'`]+$/, "");
|
|
6874
6950
|
if (value.length < 8) return false;
|
|
6875
6951
|
if (/^[A-Za-z_][A-Za-z0-9_.-]*\s*[:=]/.test(value)) return false;
|
|
6952
|
+
if (/^--?[A-Za-z]/.test(value)) return false;
|
|
6876
6953
|
if (SECRET_PLACEHOLDER_RE.test(value)) return false;
|
|
6877
6954
|
if (SECRET_CODE_EXPR_RE.test(value)) return false;
|
|
6878
6955
|
if (/^\/(?:[\[(^\\.]|.*\/[gimsuy]*$)/.test(value) || /[\[({]/.test(value)) return false;
|
|
@@ -7076,8 +7153,8 @@ function extractUrl(args, _argsText) {
|
|
|
7076
7153
|
const raw = args[field];
|
|
7077
7154
|
if (typeof raw !== "string") continue;
|
|
7078
7155
|
const shellField = ["command", "cmd", "script", "input"].includes(field);
|
|
7079
|
-
const value = shellField ? splitShellSegments(shellUrlActionText(raw)).map(withoutUnattributedJavaScriptUrls).join("\n") : raw;
|
|
7080
|
-
const firstUrl = (text) => text.match(/https?:\/\/[^\s"'
|
|
7156
|
+
const value = shellField ? splitShellSegments(shellUrlActionText(raw)).map(withoutUnattributedJavaScriptUrls).map(withoutGhMetadataProse).join("\n") : raw;
|
|
7157
|
+
const firstUrl = (text) => text.match(/https?:\/\/[^\s"'<>`]+/i)?.[0] || text.match(/\b(?:s3|gs|ftp|sftp|smb):\/\/[^\s"'<>`]+/i)?.[0];
|
|
7081
7158
|
const outboundUrls = shellField ? splitShellSegments(value).filter((segment) => /^(?:curl(?:\.exe)?|wget(?:\.exe)?|invoke-restmethod|invoke-webrequest|irm|iwr)$/i.test(shellSegmentLead(segment)) && classifyShellSegment(segment) === "write").map(firstUrl).filter((url) => Boolean(url)) : [];
|
|
7082
7159
|
const externalOutbound = outboundUrls.find((url) => {
|
|
7083
7160
|
try {
|
|
@@ -7086,15 +7163,225 @@ function extractUrl(args, _argsText) {
|
|
|
7086
7163
|
return false;
|
|
7087
7164
|
}
|
|
7088
7165
|
});
|
|
7089
|
-
const found = externalOutbound || outboundUrls[0] || firstUrl(value);
|
|
7166
|
+
const found = externalOutbound || outboundUrls[0] || firstUrl(shellField ? segmentsThatMayReachNetwork(splitShellSegments(value)).join("\n") : value);
|
|
7090
7167
|
if (found) return found;
|
|
7091
7168
|
}
|
|
7092
7169
|
return "";
|
|
7093
7170
|
}
|
|
7171
|
+
var LOCAL_DATA_LEADS = /* @__PURE__ */ new Set([
|
|
7172
|
+
// PowerShell text / file / pipeline cmdlets and their aliases
|
|
7173
|
+
"get-content",
|
|
7174
|
+
"gc",
|
|
7175
|
+
"type",
|
|
7176
|
+
"set-content",
|
|
7177
|
+
"sc",
|
|
7178
|
+
"add-content",
|
|
7179
|
+
"ac",
|
|
7180
|
+
"out-file",
|
|
7181
|
+
"out-string",
|
|
7182
|
+
"out-null",
|
|
7183
|
+
"write-host",
|
|
7184
|
+
"write-output",
|
|
7185
|
+
"write-verbose",
|
|
7186
|
+
"write-warning",
|
|
7187
|
+
"write-error",
|
|
7188
|
+
"write-information",
|
|
7189
|
+
"write-debug",
|
|
7190
|
+
"select-string",
|
|
7191
|
+
"sls",
|
|
7192
|
+
"select-object",
|
|
7193
|
+
"select",
|
|
7194
|
+
"where-object",
|
|
7195
|
+
"where",
|
|
7196
|
+
"foreach-object",
|
|
7197
|
+
"foreach",
|
|
7198
|
+
"%",
|
|
7199
|
+
"?",
|
|
7200
|
+
"sort-object",
|
|
7201
|
+
"sort",
|
|
7202
|
+
"group-object",
|
|
7203
|
+
"group",
|
|
7204
|
+
"measure-object",
|
|
7205
|
+
"measure",
|
|
7206
|
+
"compare-object",
|
|
7207
|
+
"compare",
|
|
7208
|
+
"format-table",
|
|
7209
|
+
"ft",
|
|
7210
|
+
"format-list",
|
|
7211
|
+
"fl",
|
|
7212
|
+
"format-wide",
|
|
7213
|
+
"fw",
|
|
7214
|
+
"convertto-json",
|
|
7215
|
+
"convertfrom-json",
|
|
7216
|
+
"convertto-csv",
|
|
7217
|
+
"convertfrom-csv",
|
|
7218
|
+
"test-path",
|
|
7219
|
+
"get-item",
|
|
7220
|
+
"gi",
|
|
7221
|
+
"get-childitem",
|
|
7222
|
+
"gci",
|
|
7223
|
+
"ls",
|
|
7224
|
+
"dir",
|
|
7225
|
+
"split-path",
|
|
7226
|
+
"join-path",
|
|
7227
|
+
"resolve-path",
|
|
7228
|
+
"new-item",
|
|
7229
|
+
"ni",
|
|
7230
|
+
"copy-item",
|
|
7231
|
+
"cpi",
|
|
7232
|
+
"copy",
|
|
7233
|
+
"move-item",
|
|
7234
|
+
"mi",
|
|
7235
|
+
"move",
|
|
7236
|
+
"rename-item",
|
|
7237
|
+
"rni",
|
|
7238
|
+
"ren",
|
|
7239
|
+
"remove-item",
|
|
7240
|
+
"ri",
|
|
7241
|
+
"del",
|
|
7242
|
+
"erase",
|
|
7243
|
+
"rd",
|
|
7244
|
+
"rmdir",
|
|
7245
|
+
"get-date",
|
|
7246
|
+
"set-location",
|
|
7247
|
+
"cd",
|
|
7248
|
+
"chdir",
|
|
7249
|
+
"push-location",
|
|
7250
|
+
"pushd",
|
|
7251
|
+
"pop-location",
|
|
7252
|
+
"popd",
|
|
7253
|
+
"get-location",
|
|
7254
|
+
"pwd",
|
|
7255
|
+
// POSIX text / file tools
|
|
7256
|
+
"echo",
|
|
7257
|
+
"printf",
|
|
7258
|
+
"cat",
|
|
7259
|
+
"grep",
|
|
7260
|
+
"egrep",
|
|
7261
|
+
"fgrep",
|
|
7262
|
+
"rg",
|
|
7263
|
+
"ag",
|
|
7264
|
+
"cut",
|
|
7265
|
+
"uniq",
|
|
7266
|
+
"head",
|
|
7267
|
+
"tail",
|
|
7268
|
+
"tr",
|
|
7269
|
+
"wc",
|
|
7270
|
+
"test",
|
|
7271
|
+
"mkdir",
|
|
7272
|
+
"touch",
|
|
7273
|
+
"cp",
|
|
7274
|
+
"mv",
|
|
7275
|
+
"rm",
|
|
7276
|
+
"ln",
|
|
7277
|
+
"basename",
|
|
7278
|
+
"dirname",
|
|
7279
|
+
"realpath",
|
|
7280
|
+
"jq",
|
|
7281
|
+
"yq",
|
|
7282
|
+
"diff",
|
|
7283
|
+
"date",
|
|
7284
|
+
"true",
|
|
7285
|
+
"false"
|
|
7286
|
+
]);
|
|
7287
|
+
var GIT_NETWORK_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
7288
|
+
"clone",
|
|
7289
|
+
"fetch",
|
|
7290
|
+
"pull",
|
|
7291
|
+
"push",
|
|
7292
|
+
"remote",
|
|
7293
|
+
"ls-remote",
|
|
7294
|
+
"submodule",
|
|
7295
|
+
"archive",
|
|
7296
|
+
"request-pull",
|
|
7297
|
+
"svn",
|
|
7298
|
+
"daemon",
|
|
7299
|
+
"bundle",
|
|
7300
|
+
"http-fetch",
|
|
7301
|
+
"http-push",
|
|
7302
|
+
"send-pack",
|
|
7303
|
+
"fetch-pack",
|
|
7304
|
+
"upload-pack",
|
|
7305
|
+
"receive-pack",
|
|
7306
|
+
"lfs"
|
|
7307
|
+
]);
|
|
7308
|
+
function segmentMayReachNetwork(segment) {
|
|
7309
|
+
if (/\$\(|`/.test(segment) || PS_VALUE_EXECUTOR_RE.test(segment)) return true;
|
|
7310
|
+
return splitShellSegments(segment, true).some((stage) => {
|
|
7311
|
+
if (/^\s*(?:\$[\w:]+|[A-Za-z_]\w*)\s*=/.test(stage)) return true;
|
|
7312
|
+
const masked = maskQuotedSpans(stage);
|
|
7313
|
+
for (const group of masked.matchAll(/\(\s*([A-Za-z][\w-]*)/g)) {
|
|
7314
|
+
if (!LOCAL_DATA_LEADS.has(group[1].toLowerCase())) return true;
|
|
7315
|
+
}
|
|
7316
|
+
const lead = shellSegmentLead(stage);
|
|
7317
|
+
if (!lead) return false;
|
|
7318
|
+
if (lead === "git") {
|
|
7319
|
+
const subcommand = gitSubcommand(stage);
|
|
7320
|
+
return !subcommand || GIT_NETWORK_SUBCOMMANDS.has(subcommand);
|
|
7321
|
+
}
|
|
7322
|
+
return !LOCAL_DATA_LEADS.has(lead);
|
|
7323
|
+
});
|
|
7324
|
+
}
|
|
7325
|
+
var LITERAL_ASSIGNMENT_RE = /^\s*(?:\$|(?=[A-Za-z_]))([A-Za-z_]\w*)\s*=\s*(?:@'\r?\n[\s\S]*?\r?\n'@|@"\r?\n[^$`]*?\r?\n"@|'(?:[^']|'')*'|-?\d+(?:\.\d+)?|@\((?:[\s,]|'(?:[^']|'')*'|"[^"$`]*"|@'\r?\n[\s\S]*?\r?\n'@|-?\d+(?:\.\d+)?)*\))\s*$/;
|
|
7326
|
+
function segmentsThatMayReachNetwork(segments) {
|
|
7327
|
+
const tempRedefined = segments.some((segment) => /\$env:(?:TEMP|TMP)\s*=/i.test(maskQuotedSpans(segment)));
|
|
7328
|
+
const remotePath = /(?:^|[\s"'=(])[\\/]{2}[^\s\\/"']+[\\/]/;
|
|
7329
|
+
return segments.filter((segment, index) => {
|
|
7330
|
+
if (!LITERAL_ASSIGNMENT_RE.test(segment)) return segmentMayReachNetwork(segment);
|
|
7331
|
+
return segments.slice(index + 1).some((later) => !LITERAL_ASSIGNMENT_RE.test(later) && (remotePath.test(later) || ghMetadataWriteBodyFile(tempRedefined ? later : temporaryFileSyntax(later)) === void 0 && segmentMayReachNetwork(later)));
|
|
7332
|
+
});
|
|
7333
|
+
}
|
|
7334
|
+
function gitSubcommand(stage) {
|
|
7335
|
+
const tokens = maskQuotedSpans(stage).trim().split(/\s+/);
|
|
7336
|
+
let i2 = tokens.findIndex((token) => /^(?:.*[\\/])?git(?:\.exe)?$/i.test(token)) + 1;
|
|
7337
|
+
if (i2 <= 0) return "";
|
|
7338
|
+
while (i2 < tokens.length && tokens[i2].startsWith("-")) {
|
|
7339
|
+
if (/^(?:-c|-C|--git-dir|--work-tree|--namespace|--exec-path|--config-env|--super-prefix|--list-cmds)$/i.test(tokens[i2])) i2++;
|
|
7340
|
+
i2++;
|
|
7341
|
+
}
|
|
7342
|
+
const subcommand = (tokens[i2] || "").toLowerCase();
|
|
7343
|
+
return /^[a-z][\w-]*$/.test(subcommand) ? subcommand : "";
|
|
7344
|
+
}
|
|
7345
|
+
function temporaryFileSyntax(text) {
|
|
7346
|
+
return text.replace(/"\$env:(?:TEMP|TMP)([\\/][A-Za-z0-9_ ./\\-]+)"/gi, '"fcd-temporary$1"').replace(/(?<=^|\s)\$env:(?:TEMP|TMP)([\\/][A-Za-z0-9_./\\-]+)(?=\s|$)/gi, '"fcd-temporary$1"').replace(/\(Join-Path\s+\$env:(?:TEMP|TMP)\s+'([A-Za-z0-9_ ./\\-]+)'\)/gi, '"fcd-temporary/$1"');
|
|
7347
|
+
}
|
|
7348
|
+
var GH_FLAG_VALUE_RE = /^(?:'[^'$`\r\n]*'|"[^"$`\r\n]*"|[^\s'"$`|;&(){}<>]+)$/;
|
|
7349
|
+
function ghMetadataWriteBodyFile(segment) {
|
|
7350
|
+
return parseGhMetadataWrite(segment)?.bodyFile;
|
|
7351
|
+
}
|
|
7352
|
+
function parseGhMetadataWrite(segment) {
|
|
7353
|
+
const ghBody = /^(gh\s+(?:pr\s+create|issue\s+create|pr\s+(?:comment|edit)\s+\d+|issue\s+(?:comment|edit)\s+\d+)\s+)(.+?)((?:\s+2>&1)?(?:\s*\|\s*Select-Object\s+-Last\s+\d{1,3})?)$/i.exec(segment);
|
|
7354
|
+
if (!ghBody) return void 0;
|
|
7355
|
+
const tokens = ghBody[2].match(/'[^'\r\n]*'|"[^"\r\n]*"|[^\s'"]+/g) || [];
|
|
7356
|
+
if (tokens.join("").replace(/\s/g, "") !== ghBody[2].replace(/\s/g, "")) return void 0;
|
|
7357
|
+
let bodyFile;
|
|
7358
|
+
let bodySeen = false;
|
|
7359
|
+
const kept = [];
|
|
7360
|
+
for (let i2 = 0; i2 < tokens.length; i2 += 2) {
|
|
7361
|
+
const flag = tokens[i2];
|
|
7362
|
+
const value = tokens[i2 + 1];
|
|
7363
|
+
if (!/^--(?:base|head|title|label|assignee|milestone|reviewer|body-file|body)$/i.test(flag) || !value || !GH_FLAG_VALUE_RE.test(value)) return void 0;
|
|
7364
|
+
if (/^--body-file$/i.test(flag)) {
|
|
7365
|
+
if (bodySeen) return void 0;
|
|
7366
|
+
bodySeen = true;
|
|
7367
|
+
bodyFile = value;
|
|
7368
|
+
}
|
|
7369
|
+
if (/^--body$/i.test(flag)) {
|
|
7370
|
+
if (bodySeen) return void 0;
|
|
7371
|
+
bodySeen = true;
|
|
7372
|
+
}
|
|
7373
|
+
const prose = /^--(?:body|title)$/i.test(flag);
|
|
7374
|
+
kept.push(flag, prose ? '"fcd-gh-metadata"' : value);
|
|
7375
|
+
}
|
|
7376
|
+
return { bodyFile, masked: `${ghBody[1]}${kept.join(" ")}${ghBody[3]}` };
|
|
7377
|
+
}
|
|
7378
|
+
function withoutGhMetadataProse(segment) {
|
|
7379
|
+
return parseGhMetadataWrite(segment)?.masked ?? segment;
|
|
7380
|
+
}
|
|
7094
7381
|
function shellUrlActionText(command) {
|
|
7095
7382
|
if (command.length > 65536) return command;
|
|
7096
7383
|
if (/\$env:(?:TEMP|TMP)\s*=/i.test(maskQuotedSpans(command))) return command;
|
|
7097
|
-
const fileSyntax =
|
|
7384
|
+
const fileSyntax = temporaryFileSyntax;
|
|
7098
7385
|
const originalSegments = splitShellSegments(command);
|
|
7099
7386
|
const localPaths = /* @__PURE__ */ new Map();
|
|
7100
7387
|
const literalValues = /* @__PURE__ */ new Map();
|
|
@@ -7111,7 +7398,7 @@ function shellUrlActionText(command) {
|
|
|
7111
7398
|
return value;
|
|
7112
7399
|
};
|
|
7113
7400
|
const segments = originalSegments.map((segment, index) => {
|
|
7114
|
-
const dataAssignment = /^\$([A-Za-z_][\w]*)\s*=\s*(@'\r?\n[\s\S]*?\r?\n'@|'(?:[^']|'')*')$/.exec(segment);
|
|
7401
|
+
const dataAssignment = /^\$([A-Za-z_][\w]*)\s*=\s*(@'\r?\n[\s\S]*?\r?\n'@|@"\r?\n[^$]*?\r?\n"@|'(?:[^']|'')*')$/.exec(segment);
|
|
7115
7402
|
if (dataAssignment && !literalValues.has(dataAssignment[1].toLowerCase()) && literalValues.size < 32) {
|
|
7116
7403
|
literalValues.set(dataAssignment[1].toLowerCase(), dataAssignment[2]);
|
|
7117
7404
|
dataAssignments.add(index);
|
|
@@ -7125,7 +7412,7 @@ function shellUrlActionText(command) {
|
|
|
7125
7412
|
pathAssignments.add(index);
|
|
7126
7413
|
return segment;
|
|
7127
7414
|
}
|
|
7128
|
-
const literal2 = /^(?:@'\r?\n[\s\S]*?\r?\n'@|@"\r?\n[
|
|
7415
|
+
const literal2 = /^(?:@'\r?\n[\s\S]*?\r?\n'@|@"\r?\n[^$]*?\r?\n"@|'(?:[^']|'')*'|"[^"$`]*")/.exec(segment);
|
|
7129
7416
|
const prefix = literal2?.[0] || "";
|
|
7130
7417
|
const tail = segment.slice(prefix.length).replace(
|
|
7131
7418
|
/"\$([A-Za-z_][\w]*)([\\/][\w ./\\-]*)?"/g,
|
|
@@ -7147,8 +7434,9 @@ function shellUrlActionText(command) {
|
|
|
7147
7434
|
);
|
|
7148
7435
|
});
|
|
7149
7436
|
if (expansionOverflow) return command;
|
|
7150
|
-
const
|
|
7151
|
-
|
|
7437
|
+
const storedTargets = /* @__PURE__ */ new Map();
|
|
7438
|
+
const filtered = segments.map((segment, index) => {
|
|
7439
|
+
const literal2 = /^(?:@'\r?\n[\s\S]*?\r?\n'@|@"\r?\n[^$]*?\r?\n"@|'(?:[^']|'')*'|"[^"$`]*")/;
|
|
7152
7440
|
const piped = segment.match(literal2);
|
|
7153
7441
|
let tail = piped ? segment.slice(piped[0].length).replace(/^\s*\|\s*/, "") : "";
|
|
7154
7442
|
if (!piped || !/^\s*\|/.test(segment.slice(piped[0].length))) {
|
|
@@ -7176,8 +7464,15 @@ function shellUrlActionText(command) {
|
|
|
7176
7464
|
}
|
|
7177
7465
|
target = target.replace(/^['"]|['"]$/g, "");
|
|
7178
7466
|
if (!target || /^[\\/]{2}|[\r\n*?\[\]<>]/.test(target) || /:/.test(target.replace(/^[A-Za-z]:[\\/]/, ""))) return segment;
|
|
7467
|
+
const key = target.toLowerCase();
|
|
7468
|
+
if (!storedTargets.has(key)) storedTargets.set(key, index);
|
|
7179
7469
|
return tail;
|
|
7180
7470
|
});
|
|
7471
|
+
const isStoredTarget = (token, before) => {
|
|
7472
|
+
if (!token) return false;
|
|
7473
|
+
const written = storedTargets.get(token.replace(/^['"]|['"]$/g, "").toLowerCase());
|
|
7474
|
+
return written !== void 0 && written < before;
|
|
7475
|
+
};
|
|
7181
7476
|
const onlyStoredData = segments.every((segment, index) => {
|
|
7182
7477
|
if (dataAssignments.has(index)) return true;
|
|
7183
7478
|
if (pathAssignments.has(index)) return true;
|
|
@@ -7187,6 +7482,10 @@ function shellUrlActionText(command) {
|
|
|
7187
7482
|
if (/^if\s*\(\$([A-Za-z_][\w]*)\.Count\)\s*\{\s*throw\s+\$\1\[0\]\s*\}$/i.test(segment)) return true;
|
|
7188
7483
|
if (/^Write-Output\s+'(?:[^']|'')*'$/i.test(segment)) return true;
|
|
7189
7484
|
if (/^gh\s+pr\s+checks\s+\d+\s+--json\s+[a-z,]+\s+--jq\s+'(?:[^']|'')*'$/i.test(segment)) return true;
|
|
7485
|
+
const ghBodyFile = ghMetadataWriteBodyFile(segment);
|
|
7486
|
+
if (ghBodyFile !== void 0) return isStoredTarget(ghBodyFile, index);
|
|
7487
|
+
const removal = /^Remove-Item\s+(?:-(?:LiteralPath|Path)\s+)?('[^'\r\n]*'|"[^"\r\n]*"|[^\s'"$`|;&(){}<>]+)(?:\s+-Force)?$/i.exec(segment);
|
|
7488
|
+
if (removal && isStoredTarget(removal[1], index)) return true;
|
|
7190
7489
|
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
7490
|
if (read) {
|
|
7192
7491
|
const target = read[1] ?? read[2] ?? read[3];
|
|
@@ -7223,6 +7522,22 @@ function splitShellSegments(command, alsoOnPipe = false) {
|
|
|
7223
7522
|
continue;
|
|
7224
7523
|
}
|
|
7225
7524
|
}
|
|
7525
|
+
if (ch === "@" && command[i2 + 1] === "{") {
|
|
7526
|
+
const end = hashtableEnd(command, i2);
|
|
7527
|
+
if (end > i2) {
|
|
7528
|
+
cur += command.slice(i2, end);
|
|
7529
|
+
i2 = end - 1;
|
|
7530
|
+
continue;
|
|
7531
|
+
}
|
|
7532
|
+
}
|
|
7533
|
+
if (ch === "@" && command[i2 + 1] === "(") {
|
|
7534
|
+
const end = literalArrayEnd(command, i2);
|
|
7535
|
+
if (end > i2) {
|
|
7536
|
+
cur += command.slice(i2, end);
|
|
7537
|
+
i2 = end - 1;
|
|
7538
|
+
continue;
|
|
7539
|
+
}
|
|
7540
|
+
}
|
|
7226
7541
|
if (ch === '"' || ch === "'") {
|
|
7227
7542
|
quote = ch;
|
|
7228
7543
|
cur += ch;
|
|
@@ -7263,6 +7578,47 @@ ${q}@`;
|
|
|
7263
7578
|
if (idx < 0) return -1;
|
|
7264
7579
|
return idx + close.length;
|
|
7265
7580
|
}
|
|
7581
|
+
function hashtableEnd(command, start) {
|
|
7582
|
+
let depth = 0;
|
|
7583
|
+
let quote = null;
|
|
7584
|
+
for (let i2 = start + 1; i2 < command.length; i2++) {
|
|
7585
|
+
const ch = command[i2];
|
|
7586
|
+
if (quote) {
|
|
7587
|
+
if (ch === "\\" && quote === '"' && i2 + 1 < command.length) {
|
|
7588
|
+
i2++;
|
|
7589
|
+
continue;
|
|
7590
|
+
}
|
|
7591
|
+
if (ch === quote) quote = null;
|
|
7592
|
+
continue;
|
|
7593
|
+
}
|
|
7594
|
+
if (ch === '"' || ch === "'") {
|
|
7595
|
+
quote = ch;
|
|
7596
|
+
continue;
|
|
7597
|
+
}
|
|
7598
|
+
if (ch === "{") depth++;
|
|
7599
|
+
else if (ch === "}" && --depth === 0) return i2 + 1;
|
|
7600
|
+
}
|
|
7601
|
+
return -1;
|
|
7602
|
+
}
|
|
7603
|
+
var LITERAL_ELEMENT_RE = /^(?:'(?:[^']|'')*'|"[^"$`]*"|@'\r?\n[\s\S]*?\r?\n'@|@"\r?\n[^$`]*?\r?\n"@|-?\d+(?:\.\d+)?)/;
|
|
7604
|
+
function literalArrayEnd(command, start) {
|
|
7605
|
+
let i2 = start + 2;
|
|
7606
|
+
let elements = 0;
|
|
7607
|
+
while (i2 < command.length) {
|
|
7608
|
+
const rest = command.slice(i2);
|
|
7609
|
+
const skip = /^[\s,]+/.exec(rest);
|
|
7610
|
+
if (skip) {
|
|
7611
|
+
i2 += skip[0].length;
|
|
7612
|
+
continue;
|
|
7613
|
+
}
|
|
7614
|
+
if (command[i2] === ")") return elements > 0 ? i2 + 1 : -1;
|
|
7615
|
+
const element = LITERAL_ELEMENT_RE.exec(rest);
|
|
7616
|
+
if (!element || elements >= 256) return -1;
|
|
7617
|
+
i2 += element[0].length;
|
|
7618
|
+
elements++;
|
|
7619
|
+
}
|
|
7620
|
+
return -1;
|
|
7621
|
+
}
|
|
7266
7622
|
var QUOTED_LITERAL_RE = /@'[\s\S]*?\r?\n'@|@"[\s\S]*?\r?\n"@|"((?:[^"\\]|\\.)*)"|'([^']*)'/g;
|
|
7267
7623
|
function quotedSpans(segment) {
|
|
7268
7624
|
const spans = [];
|
|
@@ -7445,6 +7801,16 @@ function shellSegmentLead(segment) {
|
|
|
7445
7801
|
}
|
|
7446
7802
|
var PS_SESSION_VARIABLE_ASSIGN_RE = /^\s*\$(?:psdefaultparametervalues|psmodulepath|profile|executioncontext|env:(?:path|pathext|psmodulepath))\s*(?:\[[^\]]*\]\s*)?(?:\+?=)/i;
|
|
7447
7803
|
var PS_VALUE_EXECUTOR_RE = /\b(?:invoke-expression|iex|invoke-command|icm|start-process|saps|invoke-item|ii)\b|&\s*[$(]|\.\s*invoke\s*\(/i;
|
|
7804
|
+
function isInertValueExpression(raw) {
|
|
7805
|
+
const masked = maskQuotedSpans(raw);
|
|
7806
|
+
if (/\$\(|`|&\s*[$(\w"']|[|<>]/.test(masked) || PS_VALUE_EXECUTOR_RE.test(masked)) return false;
|
|
7807
|
+
if (/(?:::|\.)\s*(?:invoke|start|run|exec|create|load|download|upload|send|open)\w*\s*\(/i.test(masked)) return false;
|
|
7808
|
+
for (const group of masked.matchAll(/\(\s*([A-Za-z][\w-]*)/g)) if (!LOCAL_DATA_LEADS.has(group[1].toLowerCase())) return false;
|
|
7809
|
+
const body = masked.replace(/^\s*\$[\w]+\s*(?:\+|-|\*|\/)?=\s*/, "").trim();
|
|
7810
|
+
if (/^\$\w+\s*(?:\+\+|--)$/.test(body)) return true;
|
|
7811
|
+
if (/^(?:@\{|@\()/.test(body)) return !/[$(]/.test(body.slice(2)) && /^@[{(](?:[\s;,=]|""|-?\d+(?:\.\d+)?|[\w-]+|@\{|\}|\))*$/.test(body);
|
|
7812
|
+
return /^(?:""|-?\d)/.test(body);
|
|
7813
|
+
}
|
|
7448
7814
|
var worstShellOp = (ops, floor = "read") => ops.reduce((w, op) => (SHELL_OP_RANK[op] || 0) > (SHELL_OP_RANK[w] || 0) ? op : w, floor);
|
|
7449
7815
|
function classifyShellSegment(segment, depth = 0) {
|
|
7450
7816
|
const raw = segment.trim();
|
|
@@ -7453,6 +7819,7 @@ function classifyShellSegment(segment, depth = 0) {
|
|
|
7453
7819
|
const lead = shellSegmentLead(segment);
|
|
7454
7820
|
if ((/^\$env:[\w]+$/.test(lead) || /^\$\w+$/.test(lead)) && PS_VALUE_EXECUTOR_RE.test(maskQuotedSpans(raw))) return "SHELL";
|
|
7455
7821
|
if (SHELL_NEUTRAL_BUILTINS.has(lead) || /^\$env:[\w]+$/.test(lead) || /^\$\w+$/.test(lead)) return "read";
|
|
7822
|
+
if (isInertValueExpression(raw)) return "read";
|
|
7456
7823
|
const rawLower = raw.toLowerCase();
|
|
7457
7824
|
const sqlClient = SHELL_SQL_CLIENTS.has(lead) || /\|\s*(?:sudo\s+)?(?:psql|mysql|mariadb|sqlcmd|sqlite3|mongosh?|clickhouse-client|bq|cockroach|usql)\b/.test(rawLower);
|
|
7458
7825
|
if (sqlClient) {
|
|
@@ -7484,6 +7851,8 @@ function classifyShellWords(masked, raw, lead) {
|
|
|
7484
7851
|
const explicitMethod = /(?:\s-x|\s--request|\s-method)[\s=]+["']?(post|put|patch|delete)\b/i.test(segment);
|
|
7485
7852
|
const hasBody = /\s(?:-d|--data(?:-raw|-binary|-urlencode)?|-F|--form|-T|--upload-file|--json|-body|-infile)\b/i.test(segment);
|
|
7486
7853
|
if (explicitMethod || hasBody) return "write";
|
|
7854
|
+
const credentialArgument = /\s(?:-h|--header|-headers|-u|--user|-token|-credential|-authentication|--oauth2-bearer|-proxycredential)(?:[\s=]|$)/i.exec(segment);
|
|
7855
|
+
if (credentialArgument && /\$\(|`|\$env:|\(\s*[A-Za-z][\w-]*\s/.test(segment.slice(credentialArgument.index))) return "write";
|
|
7487
7856
|
return pipesIntoInterpreter ? "SHELL" : "read";
|
|
7488
7857
|
}
|
|
7489
7858
|
if (SHELL_READ_PROGRAMS.has(lead)) {
|
|
@@ -7508,8 +7877,8 @@ function classifyShellWords(masked, raw, lead) {
|
|
|
7508
7877
|
if (/\b(drop\s+(database|table|schema)|truncate\s+table|kubectl\s+delete|terraform\s+destroy|gcloud\s+(?:sql\s+instances|compute\s+instances|projects|run\s+services)\s+delete|az\s+(?:group|vm|sql\s+server)\s+delete|aws\s+(?:s3\s+rb|rds\s+delete-db-instance|ec2\s+terminate-instances)|docker\s+(?:system\s+prune|volume\s+rm|rm\s+-f)|mkfs|dd\s+if=)\b/.test(value)) {
|
|
7509
7878
|
return "DELETE";
|
|
7510
7879
|
}
|
|
7511
|
-
const xfer = segment.toLowerCase().match(/\b(?:aws\s+s3\s+(?:cp|sync|mv)|gsutil\s+(?:-m\s+)?(?:cp|rsync|mv)|az\s+storage\s+blob\s+upload|scp|rsync|sftp)\s+(.+)$/);
|
|
7512
|
-
if (xfer && /\b(?:aws\s+s3\s+(?:cp|sync|mv)|gsutil\s+(?:-m\s+)?(?:cp|rsync|mv)|az\s+storage\s+blob\s+upload|scp|rsync|sftp)\b/.test(value)) {
|
|
7880
|
+
const xfer = segment.toLowerCase().match(/\b(?:aws\s+s3\s+(?:cp|sync|mv)|gsutil\s+(?:-m\s+)?(?:cp|rsync|mv)|gcloud(?:\.cmd|\.exe)?\s+storage\s+(?:cp|rsync|mv)|az\s+storage\s+blob\s+upload|scp|rsync|sftp)\s+(.+)$/);
|
|
7881
|
+
if (xfer && /\b(?:aws\s+s3\s+(?:cp|sync|mv)|gsutil\s+(?:-m\s+)?(?:cp|rsync|mv)|gcloud(?:\.cmd|\.exe)?\s+storage\s+(?:cp|rsync|mv)|az\s+storage\s+blob\s+upload|scp|rsync|sftp)\b/.test(value)) {
|
|
7513
7882
|
if (/az\s+storage\s+blob\s+upload/.test(value)) return "write";
|
|
7514
7883
|
const tokens = xfer[1].split(/\s+/).filter(Boolean).map((t) => t.replace(/^["']|["']$/g, ""));
|
|
7515
7884
|
const operands = [];
|
|
@@ -7640,7 +8009,7 @@ function isPowerShellResponseProjection(segment, responses) {
|
|
|
7640
8009
|
const object = /^\[pscustomobject\]\s*@\{([^{}]+)\}\s*(?:\|\s*(.+))?$/i.exec(segment);
|
|
7641
8010
|
if (!object) return false;
|
|
7642
8011
|
if (object[2] && object[2].split("|").some((part) => !isPowerShellReadFormatter(part.trim()))) return false;
|
|
7643
|
-
const fields = object[1].trim().replace(
|
|
8012
|
+
const fields = object[1].trim().replace(/[;\s]*$/, "").split(/;|\r?\n/).filter((field) => field.trim());
|
|
7644
8013
|
if (fields.length > 32) return false;
|
|
7645
8014
|
const labels = /* @__PURE__ */ new Set();
|
|
7646
8015
|
return fields.every((field) => {
|
|
@@ -7707,6 +8076,10 @@ function isInternalHost(hostname) {
|
|
|
7707
8076
|
const private172 = host.match(/^172\.(\d+)\./);
|
|
7708
8077
|
return Boolean(private172 && Number(private172[1]) >= 16 && Number(private172[1]) <= 31);
|
|
7709
8078
|
}
|
|
8079
|
+
function isSingleLabelWebHost(parsed) {
|
|
8080
|
+
const host = parsed.hostname.toLowerCase();
|
|
8081
|
+
return /^(?:https?|wss?):$/.test(parsed.protocol) && host.length > 0 && !host.includes(".") && !host.includes(":");
|
|
8082
|
+
}
|
|
7710
8083
|
function policyTargetsCall(policy, context) {
|
|
7711
8084
|
if (!policyTargetsMachineKind(policy.appliesTo, normalizeMachineKind(context.machineKind))) return false;
|
|
7712
8085
|
const developerTargets = policy.appliesTo?.developerNames?.filter(Boolean) || [];
|
|
@@ -7962,7 +8335,7 @@ function inferBundledToolContext(toolName, args) {
|
|
|
7962
8335
|
context.url = detectedUrl;
|
|
7963
8336
|
try {
|
|
7964
8337
|
const parsed = new URL(detectedUrl);
|
|
7965
|
-
const hostType = isInternalHost(parsed.hostname) ? "internal" : "external";
|
|
8338
|
+
const hostType = isInternalHost(parsed.hostname) || isSingleLabelWebHost(parsed) ? "internal" : "external";
|
|
7966
8339
|
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);
|
|
7967
8340
|
context["destination.domain"] = parsed.hostname;
|
|
7968
8341
|
context["url.risk"] = hostType;
|
|
@@ -8065,7 +8438,7 @@ function recordHttpRequestFacts(context, args, detectedUrl) {
|
|
|
8065
8438
|
if (!["http:", "https:"].includes(parsed.protocol)) return false;
|
|
8066
8439
|
const containsSecret = hasSecretLikeValue(stringifyArgs({ url: detectedUrl, headers: args.headers, body: args.body, data: args.data }));
|
|
8067
8440
|
context["request.destination.domain"] = parsed.hostname;
|
|
8068
|
-
context["request.destination.type"] = isInternalHost(parsed.hostname) ? "internal" : "external";
|
|
8441
|
+
context["request.destination.type"] = isInternalHost(parsed.hostname) || isSingleLabelWebHost(parsed) ? "internal" : "external";
|
|
8069
8442
|
context["request.containsSecret"] = containsSecret ? "true" : "false";
|
|
8070
8443
|
return containsSecret;
|
|
8071
8444
|
} catch {
|
|
@@ -8096,6 +8469,8 @@ function inventoryToolMatchesActionPolicy(toolName, toolActions, policies) {
|
|
|
8096
8469
|
}
|
|
8097
8470
|
// Annotate the CommonJS export names for ESM import in node:
|
|
8098
8471
|
0 && (module.exports = {
|
|
8472
|
+
ACTION_POLICY_GRANT_MAX_DAYS,
|
|
8473
|
+
ACTION_POLICY_GRANT_MAX_USES,
|
|
8099
8474
|
CONTENT_FACT_FIELDS,
|
|
8100
8475
|
DEFAULT_ACTION_POLICY_APPROVAL,
|
|
8101
8476
|
MACHINE_KINDS,
|
package/dist/approvalPrompt.d.ts
CHANGED
|
@@ -3,5 +3,9 @@ export interface ApprovalMessageOptions {
|
|
|
3
3
|
allowAlways?: boolean;
|
|
4
4
|
/** Human wording of the grant scope, e.g. "`gh` commands (write) to github.com". */
|
|
5
5
|
alwaysScopeLabel?: string;
|
|
6
|
+
/** How long it holds: "30 days" (default) or "7 days or 20 uses". */
|
|
7
|
+
alwaysLimitsLabel?: string;
|
|
6
8
|
}
|
|
9
|
+
/** Window title: the kind of action and the policy that stopped it, by name. */
|
|
10
|
+
export declare function nativeApprovalTitle(reason: string): string;
|
|
7
11
|
export declare function nativeApprovalMessage(reason: string, options?: ApprovalMessageOptions): string;
|