fullcourtdefense-cli 1.34.12 → 1.34.14
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.js +347 -12
- package/dist/version.json +1 -1
- package/package.json +1 -1
|
@@ -5738,6 +5738,14 @@ function parse3(input, options) {
|
|
|
5738
5738
|
}
|
|
5739
5739
|
|
|
5740
5740
|
// src/actionPolicyEngine.ts
|
|
5741
|
+
function isFirestoreQueryUrl(target) {
|
|
5742
|
+
try {
|
|
5743
|
+
const url = new URL(target);
|
|
5744
|
+
return url.origin === "https://firestore.googleapis.com" && !url.username && !url.password && !url.search && !url.hash && /^\/v1\/projects\/[^/]+\/databases\/[^/]+\/documents(?:\/[^/]+)*:runQuery$/.test(url.pathname);
|
|
5745
|
+
} catch {
|
|
5746
|
+
return false;
|
|
5747
|
+
}
|
|
5748
|
+
}
|
|
5741
5749
|
function provenJavaScriptReadUrls(code) {
|
|
5742
5750
|
if (code.length > 32768) return [];
|
|
5743
5751
|
const literal2 = (value) => ({ kind: "literal", value });
|
|
@@ -5758,6 +5766,8 @@ function provenJavaScriptReadUrls(code) {
|
|
|
5758
5766
|
const readProperty = (value, key) => {
|
|
5759
5767
|
if (["constructor", "__proto__", "prototype"].includes(key)) return fail();
|
|
5760
5768
|
if (value.kind === "object") return value.fields.get(key) || literal2(void 0);
|
|
5769
|
+
if (value.kind === "array" && key === "map") return { kind: "arrayMap", items: value.items };
|
|
5770
|
+
if (value.kind === "promise" && key === "catch") return callable("promise.catch");
|
|
5761
5771
|
if (value.kind === "response") return ["json", "text"].includes(key) ? callable(`response.${key}`) : { kind: "data" };
|
|
5762
5772
|
if (value.kind === "credential" && key === "trim") return callable("credential.trim");
|
|
5763
5773
|
if (value.kind === "string" || value.kind === "literal" && typeof value.value === "string") {
|
|
@@ -5822,24 +5832,97 @@ function provenJavaScriptReadUrls(code) {
|
|
|
5822
5832
|
const left = ev(node.left);
|
|
5823
5833
|
const right = ev(node.right);
|
|
5824
5834
|
if (node.operator !== "+") return fail();
|
|
5825
|
-
if (left.kind === "literal" && right.kind === "literal" && typeof left.value === "string" && typeof right.value === "string")
|
|
5835
|
+
if (left.kind === "literal" && right.kind === "literal" && typeof left.value === "string" && typeof right.value === "string") {
|
|
5836
|
+
if (left.value.length + right.value.length > 32768) return fail();
|
|
5837
|
+
return literal2(left.value + right.value);
|
|
5838
|
+
}
|
|
5826
5839
|
if (left.kind === "literal" && left.value === "Bearer " && right.kind === "credential") return { kind: "authorization" };
|
|
5827
5840
|
return fail();
|
|
5828
5841
|
}
|
|
5829
5842
|
case "ArrowFunctionExpression":
|
|
5830
5843
|
case "FunctionExpression":
|
|
5831
|
-
if (node.params.
|
|
5844
|
+
if (node.params.some((param) => param.type !== "Identifier") || node.generator) return fail();
|
|
5832
5845
|
return { kind: "function", node, env: new Map(env) };
|
|
5846
|
+
case "ForOfStatement": {
|
|
5847
|
+
const items = ev(node.right);
|
|
5848
|
+
if (node.await || items.kind !== "array" || items.items.length > 100 || node.left.type !== "VariableDeclaration" || node.left.kind !== "const" || node.left.declarations.length !== 1) return fail();
|
|
5849
|
+
for (const item of items.items) {
|
|
5850
|
+
const scope = new Map(env);
|
|
5851
|
+
const id = node.left.declarations[0].id;
|
|
5852
|
+
if (id.type === "Identifier") scope.set(id.name, item);
|
|
5853
|
+
else if (id.type === "ArrayPattern" && item.kind === "array" && id.elements.length === item.items.length) {
|
|
5854
|
+
id.elements.forEach((element, index) => {
|
|
5855
|
+
if (element?.type !== "Identifier") fail();
|
|
5856
|
+
scope.set(element.name, item.items[index]);
|
|
5857
|
+
});
|
|
5858
|
+
} else return fail();
|
|
5859
|
+
evaluate(node.body, scope, depth + 1);
|
|
5860
|
+
}
|
|
5861
|
+
return literal2(void 0);
|
|
5862
|
+
}
|
|
5863
|
+
case "AssignmentExpression": {
|
|
5864
|
+
if (node.operator !== "=" || node.left.type !== "MemberExpression" || node.left.computed || node.left.object.type !== "Identifier" || node.left.object.name !== "process" || env.has("process") || node.left.property.name !== "exitCode" || ev(node.right).kind !== "literal" || node.right.type !== "Literal" || node.right.value !== 1) return fail();
|
|
5865
|
+
return literal2(void 0);
|
|
5866
|
+
}
|
|
5867
|
+
case "NewExpression": {
|
|
5868
|
+
const constructor = ev(node.callee);
|
|
5869
|
+
if (constructor.kind !== "call" || constructor.name !== "GoogleAuth" || node.arguments.length !== 1) return fail();
|
|
5870
|
+
const options = ev(node.arguments[0]);
|
|
5871
|
+
if (options.kind !== "object" || options.fields.size !== 1) return fail();
|
|
5872
|
+
const scopes = options.fields.get("scopes");
|
|
5873
|
+
if (scopes?.kind !== "array" || scopes.items.length !== 1 || string(scopes.items[0]) !== "https://www.googleapis.com/auth/datastore") return fail();
|
|
5874
|
+
return object([["getClient", callable("google.getClient")]]);
|
|
5875
|
+
}
|
|
5833
5876
|
case "ReturnStatement":
|
|
5834
5877
|
return node.argument ? ev(node.argument) : literal2(void 0);
|
|
5835
5878
|
case "CallExpression": {
|
|
5836
5879
|
const callee = ev(node.callee);
|
|
5837
5880
|
const args = node.arguments.map(ev);
|
|
5838
5881
|
if (callee.kind === "function") {
|
|
5839
|
-
if (args.length) return fail();
|
|
5840
|
-
|
|
5882
|
+
if (args.length !== callee.node.params.length) return fail();
|
|
5883
|
+
const scope = new Map(callee.env);
|
|
5884
|
+
args.forEach((value, index) => scope.set(callee.node.params[index].name, value));
|
|
5885
|
+
const result = evaluate(callee.node.body, scope, depth + 1);
|
|
5886
|
+
return callee.node.async ? { kind: "promise" } : result;
|
|
5887
|
+
}
|
|
5888
|
+
if (callee.kind === "arrayMap") {
|
|
5889
|
+
if (args.length !== 1 || args[0].kind !== "function" || args[0].node.params.length !== 1 || args[0].node.async) return fail();
|
|
5890
|
+
const fn = args[0];
|
|
5891
|
+
return { kind: "array", items: callee.items.map((item) => {
|
|
5892
|
+
const scope = new Map(fn.env);
|
|
5893
|
+
scope.set(fn.node.params[0].name, item);
|
|
5894
|
+
return evaluate(fn.node.body, scope, depth + 1);
|
|
5895
|
+
}) };
|
|
5841
5896
|
}
|
|
5842
5897
|
if (callee.kind !== "call") return fail();
|
|
5898
|
+
if (callee.name === "google.getClient") {
|
|
5899
|
+
if (args.length) return fail();
|
|
5900
|
+
return object([["request", callable("google.request")]]);
|
|
5901
|
+
}
|
|
5902
|
+
if (callee.name === "google.request") {
|
|
5903
|
+
if (args.length !== 1 || args[0].kind !== "object") return fail();
|
|
5904
|
+
const fields = args[0].fields;
|
|
5905
|
+
if ([...fields.keys()].some((key) => !["url", "method", "data"].includes(key))) return fail();
|
|
5906
|
+
const target = string(fields.get("url") || fail());
|
|
5907
|
+
if (!isFirestoreQueryUrl(target) || string(fields.get("method") || fail()).toUpperCase() !== "POST") return fail();
|
|
5908
|
+
const body = fields.get("data");
|
|
5909
|
+
const passive = (value) => {
|
|
5910
|
+
if (--budget < 0) return false;
|
|
5911
|
+
if (value.kind === "literal") return true;
|
|
5912
|
+
if (value.kind === "object") return [...value.fields.values()].every(passive);
|
|
5913
|
+
return value.kind === "array" && value.items.every(passive);
|
|
5914
|
+
};
|
|
5915
|
+
if (body?.kind !== "object" || !body.fields.has("structuredQuery") || [...body.fields.keys()].some((key) => !["structuredQuery", "readTime", "transaction"].includes(key)) || !passive(body)) return fail();
|
|
5916
|
+
urls.push(target);
|
|
5917
|
+
return { kind: "data" };
|
|
5918
|
+
}
|
|
5919
|
+
if (callee.name === "promise.catch") {
|
|
5920
|
+
if (args.length !== 1 || args[0].kind !== "function" || args[0].node.params.length !== 1) return fail();
|
|
5921
|
+
const scope = new Map(args[0].env);
|
|
5922
|
+
scope.set(args[0].node.params[0].name, { kind: "data" });
|
|
5923
|
+
evaluate(args[0].node.body, scope, depth + 1);
|
|
5924
|
+
return { kind: "data" };
|
|
5925
|
+
}
|
|
5843
5926
|
if (callee.name === "fetch") {
|
|
5844
5927
|
if (args.length < 1 || args.length > 2) return fail();
|
|
5845
5928
|
const target = string(args[0]);
|
|
@@ -5866,6 +5949,7 @@ function provenJavaScriptReadUrls(code) {
|
|
|
5866
5949
|
return { kind: "response" };
|
|
5867
5950
|
}
|
|
5868
5951
|
if (callee.name === "require") {
|
|
5952
|
+
if (args.length === 1 && string(args[0]) === "google-auth-library") return object([["GoogleAuth", callable("GoogleAuth")]]);
|
|
5869
5953
|
if (args.length !== 1 || !["node:child_process", "child_process"].includes(string(args[0]))) return fail();
|
|
5870
5954
|
return object([["execFileSync", callable("execFileSync")]]);
|
|
5871
5955
|
}
|
|
@@ -5888,7 +5972,7 @@ function provenJavaScriptReadUrls(code) {
|
|
|
5888
5972
|
if (args.length !== 1 || !jsonSafe(args[0])) return fail();
|
|
5889
5973
|
return { kind: "string" };
|
|
5890
5974
|
}
|
|
5891
|
-
if (callee.name === "console.log") return literal2(void 0);
|
|
5975
|
+
if (callee.name === "console.log" || callee.name === "console.error") return literal2(void 0);
|
|
5892
5976
|
if (callee.name === "string.trim") {
|
|
5893
5977
|
if (args.length) return fail();
|
|
5894
5978
|
return { kind: "string" };
|
|
@@ -5913,14 +5997,14 @@ function provenJavaScriptReadUrls(code) {
|
|
|
5913
5997
|
["fetch", callable("fetch")],
|
|
5914
5998
|
["require", callable("require")],
|
|
5915
5999
|
["JSON", object([["stringify", callable("JSON.stringify")]])],
|
|
5916
|
-
["console", object([["log", callable("console.log")]])]
|
|
6000
|
+
["console", object([["log", callable("console.log")], ["error", callable("console.error")]])]
|
|
5917
6001
|
]));
|
|
5918
6002
|
return urls;
|
|
5919
6003
|
} catch {
|
|
5920
6004
|
return [];
|
|
5921
6005
|
}
|
|
5922
6006
|
}
|
|
5923
|
-
function
|
|
6007
|
+
function provenInlineJavaScriptReadUrls(command) {
|
|
5924
6008
|
const urls = [];
|
|
5925
6009
|
for (const segment of splitShellSegments(command)) {
|
|
5926
6010
|
const here = segment.match(/^\s*@'\r?\n([\s\S]*?)\r?\n'@\s*\|\s*node(?:\.exe)?(?:\s+-)?\s*$/i);
|
|
@@ -5928,14 +6012,197 @@ function provenInlineJavaScriptRead(command, detectedUrl) {
|
|
|
5928
6012
|
const code = here?.[1] ?? inline?.[1];
|
|
5929
6013
|
if (code !== void 0) {
|
|
5930
6014
|
const proven = provenJavaScriptReadUrls(code);
|
|
5931
|
-
if (!proven.length) return
|
|
6015
|
+
if (!proven.length) return [];
|
|
5932
6016
|
urls.push(...proven);
|
|
5933
6017
|
} else {
|
|
5934
6018
|
const lead = shellSegmentLead(segment);
|
|
5935
|
-
if (!(SHELL_READ_PROGRAMS.has(lead) || ["git", "gh", "cd", "set-location"].includes(lead)) || classifyShellCommandOperation(segment) !== "read" || /https?:\/\//i.test(segment)) return
|
|
6019
|
+
if (!(SHELL_READ_PROGRAMS.has(lead) || ["git", "gh", "cd", "set-location"].includes(lead)) || classifyShellCommandOperation(segment) !== "read" || /https?:\/\//i.test(segment)) return [];
|
|
5936
6020
|
}
|
|
5937
6021
|
}
|
|
5938
|
-
return urls
|
|
6022
|
+
return urls;
|
|
6023
|
+
}
|
|
6024
|
+
function provenPowerShellQueryUrls(command) {
|
|
6025
|
+
if (command.length > 32768) return [];
|
|
6026
|
+
const prefix = /^\s*(?:git\s+status(?:\s+--short)?(?:\s+--branch)?|(?:cd|Set-Location)\s+(?:[A-Za-z]:[\\/][A-Za-z0-9_./\\-]+|'[A-Za-z]:[\\/][A-Za-z0-9_ ./\\-]+'))\s*(?:&&|;)\s*/i;
|
|
6027
|
+
for (let count = 0; count < 8 && prefix.test(command); count++) command = command.replace(prefix, "");
|
|
6028
|
+
const credential = Object.freeze({ credential: 1 });
|
|
6029
|
+
const authorization = Object.freeze({ authorization: 1 });
|
|
6030
|
+
const urls = [];
|
|
6031
|
+
const env = /* @__PURE__ */ new Map();
|
|
6032
|
+
const tokens = [];
|
|
6033
|
+
const fail = () => {
|
|
6034
|
+
throw new Error("Unsupported PowerShell query");
|
|
6035
|
+
};
|
|
6036
|
+
const lexer = /[ \t]+|\r?\n|@\{|@\(|'(?:[^']|'')*'|"[^"`]*"|\$[A-Za-z_][A-Za-z0-9_]*|-[A-Za-z]+|[A-Za-z_][A-Za-z0-9_-]*|\d+|[{}();=+|,]/gy;
|
|
6037
|
+
let offset2 = 0;
|
|
6038
|
+
let budget = 8e3;
|
|
6039
|
+
let pos = 0;
|
|
6040
|
+
const take = () => {
|
|
6041
|
+
if (--budget < 0 || pos >= tokens.length) return fail();
|
|
6042
|
+
return tokens[pos++];
|
|
6043
|
+
};
|
|
6044
|
+
const peek = () => tokens[pos];
|
|
6045
|
+
const expect = (token) => {
|
|
6046
|
+
if (take().toLowerCase() !== token.toLowerCase()) fail();
|
|
6047
|
+
};
|
|
6048
|
+
const separators = () => {
|
|
6049
|
+
while (peek() === ";") take();
|
|
6050
|
+
};
|
|
6051
|
+
const passive = (value) => {
|
|
6052
|
+
if (--budget < 0 || value === credential || value === authorization) return false;
|
|
6053
|
+
if (typeof value !== "object") return true;
|
|
6054
|
+
return Object.values(value).every(passive);
|
|
6055
|
+
};
|
|
6056
|
+
const expression = () => {
|
|
6057
|
+
const token = take();
|
|
6058
|
+
let value;
|
|
6059
|
+
if (token.startsWith("'")) value = token.slice(1, -1).replace(/''/g, "'");
|
|
6060
|
+
else if (token.startsWith('"')) {
|
|
6061
|
+
const text = token.slice(1, -1);
|
|
6062
|
+
const bearer = text.match(/^Bearer (\$[A-Za-z_][A-Za-z0-9_]*)$/);
|
|
6063
|
+
if (bearer && env.get(bearer[1].toLowerCase()) === credential) value = authorization;
|
|
6064
|
+
else {
|
|
6065
|
+
if (/[$`]/.test(text)) return fail();
|
|
6066
|
+
value = text;
|
|
6067
|
+
}
|
|
6068
|
+
} else if (token.startsWith("$")) value = env.get(token.toLowerCase()) ?? fail();
|
|
6069
|
+
else if (/^\d+$/.test(token)) value = Number(token);
|
|
6070
|
+
else if (token === "(") {
|
|
6071
|
+
value = expression();
|
|
6072
|
+
expect(")");
|
|
6073
|
+
} else if (token === "@(") {
|
|
6074
|
+
const items = [];
|
|
6075
|
+
separators();
|
|
6076
|
+
while (peek() !== ")") {
|
|
6077
|
+
items.push(expression());
|
|
6078
|
+
separators();
|
|
6079
|
+
if (peek() !== ")") expect(",");
|
|
6080
|
+
}
|
|
6081
|
+
expect(")");
|
|
6082
|
+
value = items;
|
|
6083
|
+
} else if (token === "@{") {
|
|
6084
|
+
const fields = /* @__PURE__ */ Object.create(null);
|
|
6085
|
+
separators();
|
|
6086
|
+
while (peek() !== "}") {
|
|
6087
|
+
const key = take();
|
|
6088
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key) || Object.hasOwn(fields, key)) return fail();
|
|
6089
|
+
expect("=");
|
|
6090
|
+
fields[key] = expression();
|
|
6091
|
+
if (peek() !== "}") expect(";");
|
|
6092
|
+
separators();
|
|
6093
|
+
}
|
|
6094
|
+
expect("}");
|
|
6095
|
+
value = fields;
|
|
6096
|
+
} else if (token.toLowerCase() === "if") {
|
|
6097
|
+
expect("(");
|
|
6098
|
+
const left = expression();
|
|
6099
|
+
expect("-eq");
|
|
6100
|
+
const right = expression();
|
|
6101
|
+
expect(")");
|
|
6102
|
+
if (typeof left === "object" || typeof right === "object") return fail();
|
|
6103
|
+
expect("{");
|
|
6104
|
+
const yes = expression();
|
|
6105
|
+
expect("}");
|
|
6106
|
+
expect("else");
|
|
6107
|
+
expect("{");
|
|
6108
|
+
const no = expression();
|
|
6109
|
+
expect("}");
|
|
6110
|
+
value = left === right ? yes : no;
|
|
6111
|
+
} else if (token.toLowerCase() === "gcloud") {
|
|
6112
|
+
expect("auth");
|
|
6113
|
+
expect("print-access-token");
|
|
6114
|
+
value = credential;
|
|
6115
|
+
} else return fail();
|
|
6116
|
+
while (peek() === "+") {
|
|
6117
|
+
take();
|
|
6118
|
+
const right = expression();
|
|
6119
|
+
if (typeof value !== "string" || typeof right !== "string" || value.length + right.length > 32768) return fail();
|
|
6120
|
+
value += right;
|
|
6121
|
+
}
|
|
6122
|
+
if (peek() === "|") {
|
|
6123
|
+
take();
|
|
6124
|
+
expect("ConvertTo-Json");
|
|
6125
|
+
expect("-Depth");
|
|
6126
|
+
const depth = take();
|
|
6127
|
+
if (!/^\d+$/.test(depth) || Number(depth) > 100 || !passive(value)) return fail();
|
|
6128
|
+
}
|
|
6129
|
+
return value;
|
|
6130
|
+
};
|
|
6131
|
+
const statement = () => {
|
|
6132
|
+
if (peek()?.startsWith("$")) {
|
|
6133
|
+
const name = take().toLowerCase();
|
|
6134
|
+
expect("=");
|
|
6135
|
+
if (["$env", "$psdefaultparametervalues", "$executioncontext", "$erroractionpreference", "$home", "$profile"].includes(name)) return fail();
|
|
6136
|
+
env.set(name, expression());
|
|
6137
|
+
return;
|
|
6138
|
+
}
|
|
6139
|
+
if (peek()?.toLowerCase() === "foreach") {
|
|
6140
|
+
take();
|
|
6141
|
+
expect("(");
|
|
6142
|
+
const name = take().toLowerCase();
|
|
6143
|
+
if (!/^\$[a-z_][a-z0-9_]*$/.test(name)) return fail();
|
|
6144
|
+
expect("in");
|
|
6145
|
+
const items = expression();
|
|
6146
|
+
expect(")");
|
|
6147
|
+
expect("{");
|
|
6148
|
+
if (!Array.isArray(items) || !items.length || items.length > 100) return fail();
|
|
6149
|
+
const start = pos;
|
|
6150
|
+
for (const item of items) {
|
|
6151
|
+
env.set(name, item);
|
|
6152
|
+
pos = start;
|
|
6153
|
+
separators();
|
|
6154
|
+
while (peek() !== "}") {
|
|
6155
|
+
statement();
|
|
6156
|
+
if (peek() !== "}") expect(";");
|
|
6157
|
+
separators();
|
|
6158
|
+
}
|
|
6159
|
+
expect("}");
|
|
6160
|
+
}
|
|
6161
|
+
return;
|
|
6162
|
+
}
|
|
6163
|
+
expect("Invoke-RestMethod");
|
|
6164
|
+
const options = /* @__PURE__ */ new Map();
|
|
6165
|
+
while (peek()?.startsWith("-")) {
|
|
6166
|
+
const flag = take().toLowerCase();
|
|
6167
|
+
if (options.has(flag)) return fail();
|
|
6168
|
+
options.set(flag, flag === "-method" ? take() : expression());
|
|
6169
|
+
}
|
|
6170
|
+
if ([...options.keys()].some((key) => !["-method", "-uri", "-headers", "-contenttype", "-body"].includes(key))) return fail();
|
|
6171
|
+
const target = options.get("-uri");
|
|
6172
|
+
if (typeof target !== "string") return fail();
|
|
6173
|
+
if (!isFirestoreQueryUrl(target) || String(options.get("-method")).toUpperCase() !== "POST" || options.get("-contenttype") !== "application/json") return fail();
|
|
6174
|
+
const headers = options.get("-headers");
|
|
6175
|
+
const body = options.get("-body");
|
|
6176
|
+
if (!headers || typeof headers !== "object" || Array.isArray(headers) || Object.keys(headers).length !== 1 || headers.Authorization !== authorization) return fail();
|
|
6177
|
+
if (!body || typeof body !== "object" || Array.isArray(body) || !Object.hasOwn(body, "structuredQuery") || Object.keys(body).some((key) => !["structuredQuery", "transaction", "readTime"].includes(key)) || !passive(body)) return fail();
|
|
6178
|
+
urls.push(target);
|
|
6179
|
+
if (peek() === "|") {
|
|
6180
|
+
take();
|
|
6181
|
+
expect("ConvertTo-Json");
|
|
6182
|
+
expect("-Depth");
|
|
6183
|
+
if (!/^\d+$/.test(take())) fail();
|
|
6184
|
+
}
|
|
6185
|
+
};
|
|
6186
|
+
try {
|
|
6187
|
+
while (offset2 < command.length) {
|
|
6188
|
+
lexer.lastIndex = offset2;
|
|
6189
|
+
const match = lexer.exec(command);
|
|
6190
|
+
if (!match) return [];
|
|
6191
|
+
offset2 = lexer.lastIndex;
|
|
6192
|
+
const token = match[0];
|
|
6193
|
+
if (/^[ \t]+$/.test(token)) continue;
|
|
6194
|
+
tokens.push(/\r?\n/.test(token) && token.length <= 2 ? ";" : token);
|
|
6195
|
+
}
|
|
6196
|
+
separators();
|
|
6197
|
+
while (pos < tokens.length) {
|
|
6198
|
+
statement();
|
|
6199
|
+
if (pos < tokens.length) expect(";");
|
|
6200
|
+
separators();
|
|
6201
|
+
}
|
|
6202
|
+
return urls;
|
|
6203
|
+
} catch {
|
|
6204
|
+
return [];
|
|
6205
|
+
}
|
|
5939
6206
|
}
|
|
5940
6207
|
function approvalExceptionError(rule) {
|
|
5941
6208
|
const ids = rule.approval?.replacesPolicyIds;
|
|
@@ -6445,6 +6712,7 @@ var OPERATION_MATCH_SKIP_FIELDS = /* @__PURE__ */ new Set([
|
|
|
6445
6712
|
"destination.domain",
|
|
6446
6713
|
"destination.host",
|
|
6447
6714
|
"destination.type",
|
|
6715
|
+
"destination.repository",
|
|
6448
6716
|
"destinationHost",
|
|
6449
6717
|
"host",
|
|
6450
6718
|
"hostname",
|
|
@@ -6643,6 +6911,63 @@ function hasPiiLikeValue(raw) {
|
|
|
6643
6911
|
return false;
|
|
6644
6912
|
}
|
|
6645
6913
|
var URL_ACTION_FIELDS = ["command", "cmd", "script", "input", "target", "address", "host", "server", "baseUrl", "base_url", "apiUrl", "api_url", "remote", "repo", "repository", "source", "destination"];
|
|
6914
|
+
function explicitGitHubWriteRepository(command) {
|
|
6915
|
+
if (command.length > 65536 || /[%!]/.test(command)) return void 0;
|
|
6916
|
+
const segments = splitShellSegments(command);
|
|
6917
|
+
if (segments.length !== 1) return void 0;
|
|
6918
|
+
const segment = segments[0];
|
|
6919
|
+
const tokens = [];
|
|
6920
|
+
const token = /[ \t]*(?:'([^'\r\n]*)'|"([^"$`\\\r\n]*)"|([^\s'"$`\\|;&<>(){}\r\n]+))/gy;
|
|
6921
|
+
let offset2 = 0;
|
|
6922
|
+
while (offset2 < segment.length) {
|
|
6923
|
+
token.lastIndex = offset2;
|
|
6924
|
+
const match = token.exec(segment);
|
|
6925
|
+
if (!match || token.lastIndex < segment.length && !/[ \t]/.test(segment[token.lastIndex])) return void 0;
|
|
6926
|
+
if (match[3] !== void 0 && (!/^[a-z0-9_./:@=+-]+$/i.test(match[3]) || match[3].startsWith("@"))) return void 0;
|
|
6927
|
+
if (match[1] !== void 0 && /[|;&<>()$`^]/.test(match[1])) return void 0;
|
|
6928
|
+
tokens.push(match[1] ?? match[2] ?? match[3]);
|
|
6929
|
+
offset2 = token.lastIndex;
|
|
6930
|
+
}
|
|
6931
|
+
if (!/^gh(?:\.exe)?$/i.test(tokens[0] || "")) return void 0;
|
|
6932
|
+
let repository;
|
|
6933
|
+
let resource;
|
|
6934
|
+
let operation;
|
|
6935
|
+
let positional;
|
|
6936
|
+
const canonical = (value) => {
|
|
6937
|
+
const match = /^([a-z0-9][a-z0-9.-]*\.[a-z0-9-]+)\/([a-z0-9][a-z0-9-]*)\/([a-z0-9_.-]+)$/i.exec(value);
|
|
6938
|
+
if (!match || /^(?:\.|\.\.)$/.test(match[3]) || /\.git$/i.test(match[3])) return void 0;
|
|
6939
|
+
return `${match[1]}/${match[2]}/${match[3]}`.toLowerCase();
|
|
6940
|
+
};
|
|
6941
|
+
for (let i2 = 1; i2 < tokens.length; i2++) {
|
|
6942
|
+
const value = tokens[i2];
|
|
6943
|
+
if (value === "-R" || value === "--repo" || value.startsWith("--repo=") || /^-R.+/.test(value)) {
|
|
6944
|
+
if (repository) return void 0;
|
|
6945
|
+
const selector = value === "-R" || value === "--repo" ? tokens[++i2] : value.replace(/^(?:--repo=|-R)/, "");
|
|
6946
|
+
repository = canonical(selector || "");
|
|
6947
|
+
if (!repository) return void 0;
|
|
6948
|
+
} else if (["--body", "-b", "--body-file", "-F", "--title", "-t"].includes(value)) {
|
|
6949
|
+
if (!tokens[++i2]) return void 0;
|
|
6950
|
+
} else if (/^--(?:body|body-file|title)=.+/.test(value)) {
|
|
6951
|
+
continue;
|
|
6952
|
+
} else if (["--edit-last", "--create-if-none", "--delete-last", "--yes"].includes(value)) {
|
|
6953
|
+
continue;
|
|
6954
|
+
} else if (value.startsWith("-")) {
|
|
6955
|
+
return void 0;
|
|
6956
|
+
} else if (!resource) resource = value;
|
|
6957
|
+
else if (!operation) operation = value;
|
|
6958
|
+
else if (!positional) positional = value;
|
|
6959
|
+
else return void 0;
|
|
6960
|
+
}
|
|
6961
|
+
if (!["pr", "issue"].includes(resource || "") || !["comment", "create", "edit"].includes(operation || "")) return void 0;
|
|
6962
|
+
if (positional?.startsWith("https://")) {
|
|
6963
|
+
const target = /^https:\/\/([^/]+)\/([^/]+)\/([^/]+)\/(pull|issues)\/[1-9][0-9]*\/?$/.exec(positional);
|
|
6964
|
+
if (!target || (resource === "pr" ? target[4] !== "pull" : target[4] !== "issues")) return void 0;
|
|
6965
|
+
const fromUrl = canonical(`${target[1]}/${target[2]}/${target[3]}`);
|
|
6966
|
+
if (!fromUrl || repository && repository !== fromUrl) return void 0;
|
|
6967
|
+
repository = fromUrl;
|
|
6968
|
+
} else if (positional && !/^[1-9][0-9]*$/.test(positional)) return void 0;
|
|
6969
|
+
return repository;
|
|
6970
|
+
}
|
|
6646
6971
|
function extractUrl(args, _argsText) {
|
|
6647
6972
|
const direct = args.url || args.uri || args.endpoint || args.webhookUrl || args.callbackUrl || args.href || args.link;
|
|
6648
6973
|
if (typeof direct === "string" && direct.trim()) return direct.trim();
|
|
@@ -7282,13 +7607,14 @@ function inferToolContext(toolName, args) {
|
|
|
7282
7607
|
operation = methodArg;
|
|
7283
7608
|
if (args.url) context.url = args.url;
|
|
7284
7609
|
}
|
|
7285
|
-
const
|
|
7610
|
+
const provenReadUrls = isCodeExecTool && !hasSecretLikeValue(argsText) ? [...provenInlineJavaScriptReadUrls(commandText), ...provenPowerShellQueryUrls(commandText)] : [];
|
|
7611
|
+
const detectedUrl = provenReadUrls[0] || extractUrl(args, argsText);
|
|
7286
7612
|
if (detectedUrl) {
|
|
7287
7613
|
context.url = detectedUrl;
|
|
7288
7614
|
try {
|
|
7289
7615
|
const parsed = new URL(detectedUrl);
|
|
7290
7616
|
const hostType = isInternalHost(parsed.hostname) ? "internal" : "external";
|
|
7291
|
-
const inbound = operation === "read" || ["GET", "HEAD", "OPTIONS", "SELECT", "SHOW", "DESCRIBE", "EXPLAIN"].includes(operation) || isCodeExecTool && !hasSecretLikeValue(argsText) && provenCompoundDownload(commandText, detectedUrl) ||
|
|
7617
|
+
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);
|
|
7292
7618
|
context["destination.domain"] = parsed.hostname;
|
|
7293
7619
|
context["url.risk"] = hostType;
|
|
7294
7620
|
if (inbound) {
|
|
@@ -7316,6 +7642,15 @@ function inferToolContext(toolName, args) {
|
|
|
7316
7642
|
context.amount = String(args.amount ?? args.price ?? args.total);
|
|
7317
7643
|
}
|
|
7318
7644
|
context["action.kind"] = isCodeExecTool || toolNameLower === "shell" ? "shell" : context.query !== void 0 && operation !== toolName && /^(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|TRUNCATE|GRANT|REVOKE|SHOW|DESCRIBE|EXPLAIN)$/.test(operation) ? "database" : ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"].includes(methodArg) ? "http" : context.path !== void 0 && (operation === "read" || operation === "write" || LOCAL_FILE_ACTION_RE.test(toolName) || FILE_REMOVAL_TOOL_RE.test(toolName)) ? "file" : "tool";
|
|
7645
|
+
delete context["destination.repository"];
|
|
7646
|
+
if (isCodeExecTool) {
|
|
7647
|
+
const repository = explicitGitHubWriteRepository(String(args.command || args.cmd || args.code || args.script || args.input || ""));
|
|
7648
|
+
if (repository) {
|
|
7649
|
+
context["destination.repository"] = repository;
|
|
7650
|
+
context["destination.domain"] = repository.split("/")[0];
|
|
7651
|
+
context["destination.type"] = isInternalHost(context["destination.domain"]) ? "internal" : "external";
|
|
7652
|
+
}
|
|
7653
|
+
}
|
|
7319
7654
|
return { operation, context };
|
|
7320
7655
|
}
|
|
7321
7656
|
function inferInventoryToolOperations(toolName, toolActions = []) {
|
package/dist/version.json
CHANGED