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.
@@ -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") return literal2(left.value + right.value);
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.length || node.generator) return fail();
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
- return evaluate(callee.node.body, new Map(callee.env), depth + 1);
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 provenInlineJavaScriptRead(command, detectedUrl) {
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 false;
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 false;
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.includes(detectedUrl);
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 detectedUrl = extractUrl(args, argsText);
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) || operation === "SHELL" && isCodeExecTool && !hasSecretLikeValue(argsText) && provenInlineJavaScriptRead(commandText, 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);
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
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "1.34.12"
2
+ "version": "1.34.14"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fullcourtdefense-cli",
3
- "version": "1.34.12",
3
+ "version": "1.34.14",
4
4
  "description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
5
5
  "main": "dist/index.js",
6
6
  "bin": {