fullcourtdefense-cli 1.34.11 → 1.34.12

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.
@@ -6458,7 +6458,9 @@ var OPERATION_MATCH_SKIP_FIELDS = /* @__PURE__ */ new Set([
6458
6458
  function buildOperationMatchText(_toolName, context) {
6459
6459
  const parts = [];
6460
6460
  for (const [key, value] of Object.entries(context)) {
6461
- if (OPERATION_MATCH_SKIP_FIELDS.has(key)) continue;
6461
+ const fieldWords = key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().split(/[_.-]/);
6462
+ const identifierField = /^(?:id|ids|uuid|uuids|guid|guids)$/.test(fieldWords[fieldWords.length - 1]);
6463
+ if (OPERATION_MATCH_SKIP_FIELDS.has(key) || identifierField) continue;
6462
6464
  if (typeof value !== "string" || value.length === 0 || value.length > 2e3) continue;
6463
6465
  const text = COMMAND_LINE_FIELDS.has(key) ? shellVerbWords(value) : value;
6464
6466
  parts.push(`${key} ${text}`);
@@ -6647,8 +6649,18 @@ function extractUrl(args, _argsText) {
6647
6649
  for (const field of URL_ACTION_FIELDS) {
6648
6650
  const raw = args[field];
6649
6651
  if (typeof raw !== "string") continue;
6650
- const value = ["command", "cmd", "script", "input"].includes(field) ? shellUrlActionText(raw) : raw;
6651
- const found = value.match(/https?:\/\/[^\s"'<>]+/i)?.[0] || value.match(/\b(?:s3|gs|ftp|sftp|smb):\/\/[^\s"'<>]+/i)?.[0];
6652
+ const shellField = ["command", "cmd", "script", "input"].includes(field);
6653
+ const value = shellField ? shellUrlActionText(raw) : raw;
6654
+ const firstUrl = (text) => text.match(/https?:\/\/[^\s"'<>]+/i)?.[0] || text.match(/\b(?:s3|gs|ftp|sftp|smb):\/\/[^\s"'<>]+/i)?.[0];
6655
+ 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)) : [];
6656
+ const externalOutbound = outboundUrls.find((url) => {
6657
+ try {
6658
+ return !isInternalHost(new URL(url).hostname);
6659
+ } catch {
6660
+ return false;
6661
+ }
6662
+ });
6663
+ const found = externalOutbound || outboundUrls[0] || firstUrl(value);
6652
6664
  if (found) return found;
6653
6665
  }
6654
6666
  return "";
@@ -7018,6 +7030,44 @@ function classifyShellCommandOperation(command, depth = 0) {
7018
7030
  if (segments.length === 0) return "SHELL";
7019
7031
  return worstShellOp(segments.map((segment) => classifyShellSegment(segment, depth)), "read");
7020
7032
  }
7033
+ function provenCompoundDownload(command, detectedUrl) {
7034
+ const segments = splitShellSegments(command).filter((segment) => /\b(?:https?|s3|gs|ftp|sftp|smb):\/\//i.test(segment));
7035
+ if (!segments.length || !command.includes(detectedUrl)) return false;
7036
+ return segments.every((segment) => {
7037
+ if (/[$`{}()<>;&]/.test(segment)) return false;
7038
+ const pipeline = splitShellSegments(segment, true);
7039
+ if (pipeline.slice(1).some((part) => !/^(?:Select-Object|Format-Table|Format-List)\s+[\w., *-]+$|^Out-Null$/i.test(part))) return false;
7040
+ const raw = pipeline[0];
7041
+ const pieces = raw.match(/"[^"\r\n]*"|'[^'\r\n]*'|[^\s"']+/g) || [];
7042
+ if (pieces.join("").replace(/\s/g, "") !== raw.replace(/\s/g, "")) return false;
7043
+ const tokens = pieces.map((piece) => piece.replace(/^["']|["']$/g, ""));
7044
+ if (!/^(?:curl(?:\.exe)?|wget(?:\.exe)?|Invoke-RestMethod|Invoke-WebRequest|irm|iwr)$/i.test(tokens.shift() || "")) return false;
7045
+ let urls = 0;
7046
+ for (let i2 = 0; i2 < tokens.length; i2++) {
7047
+ const token = tokens[i2];
7048
+ if (/^https?:\/\//i.test(token)) {
7049
+ urls++;
7050
+ continue;
7051
+ }
7052
+ if (/^-Uri$/i.test(token) && /^https?:\/\//i.test(tokens[i2 + 1] || "")) {
7053
+ urls++;
7054
+ i2++;
7055
+ continue;
7056
+ }
7057
+ if (/^(?:-X|--request|-Method)$/.test(token) && /^(?:GET|HEAD|OPTIONS)$/i.test(tokens[i2 + 1] || "")) {
7058
+ i2++;
7059
+ continue;
7060
+ }
7061
+ if (/^-[sfSLI]+$|^--(?:silent|fail|location|head)$|^-UseBasicParsing$/.test(token)) continue;
7062
+ if (/^-TimeoutSec$/i.test(token) && /^\d+$/.test(tokens[i2 + 1] || "")) {
7063
+ i2++;
7064
+ continue;
7065
+ }
7066
+ return false;
7067
+ }
7068
+ return urls === 1;
7069
+ });
7070
+ }
7021
7071
  function isInternalHost(hostname) {
7022
7072
  const host = hostname.toLowerCase();
7023
7073
  if (!host || host === "localhost" || host === "::1" || host.endsWith(".local") || host.endsWith(".internal")) return true;
@@ -7175,6 +7225,7 @@ function inferToolContext(toolName, args) {
7175
7225
  let operation = toolName;
7176
7226
  const toolNameLower = toolName.toLowerCase();
7177
7227
  const argsText = stringifyArgs(args);
7228
+ const commandText = String(args.command || args.cmd || args.code || args.script || args.input || "");
7178
7229
  const retryCount = Number(args.retryCount ?? args.retries ?? args.attempts ?? 0);
7179
7230
  const fanoutCount = Number(args.fanoutCount ?? args.recipientCount ?? args.childCalls ?? (Array.isArray(args.recipients) ? args.recipients.length : 0));
7180
7231
  const burstCount = Math.max(
@@ -7214,7 +7265,7 @@ function inferToolContext(toolName, args) {
7214
7265
  }
7215
7266
  }
7216
7267
  if (operation === toolName && CODE_EXEC_TOOL_NAME_RE.test(nameWords)) {
7217
- operation = classifyShellCommandOperation(String(args.command || args.code || args.script || args.input || ""));
7268
+ operation = classifyShellCommandOperation(commandText);
7218
7269
  }
7219
7270
  const pathArg = args.path || args.filepath || args.file || args.filename || "";
7220
7271
  if (typeof pathArg === "string" && pathArg.length > 0) {
@@ -7237,7 +7288,7 @@ function inferToolContext(toolName, args) {
7237
7288
  try {
7238
7289
  const parsed = new URL(detectedUrl);
7239
7290
  const hostType = isInternalHost(parsed.hostname) ? "internal" : "external";
7240
- const inbound = operation === "read" || ["GET", "HEAD", "OPTIONS", "SELECT", "SHOW", "DESCRIBE", "EXPLAIN"].includes(operation) || operation === "SHELL" && isCodeExecTool && !hasSecretLikeValue(argsText) && provenInlineJavaScriptRead(String(args.command || args.code || args.script || args.input || ""), 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);
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);
7241
7292
  context["destination.domain"] = parsed.hostname;
7242
7293
  context["url.risk"] = hostType;
7243
7294
  if (inbound) {
package/dist/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "1.34.11"
2
+ "version": "1.34.12"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fullcourtdefense-cli",
3
- "version": "1.34.11",
3
+ "version": "1.34.12",
4
4
  "description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
5
5
  "main": "dist/index.js",
6
6
  "bin": {