fullcourtdefense-cli 1.34.7 → 1.34.8

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.
@@ -96,6 +96,15 @@ exports.CONTENT_FACT_FIELDS = new Set([
96
96
  ]);
97
97
  /** Tool / operation names that mean "a file on this machine": read_file, edit_file, write_file, create_file, append_to_file, str_replace, ... */
98
98
  const LOCAL_FILE_ACTION_RE = /(?:^|_)(?:read|write|edit|create|append|save|touch|patch|replace|insert|update)(?:_(?:to|in|into))?_?(?:file|text|lines?|contents?)s?(?:$|_)|^(?:read|write|edit)$|^str_replace$/i;
99
+ /**
100
+ * Tool names that remove a FILE (delete_file, remove_file, unlink, rm_file, the
101
+ * IDE Delete mapped to delete_file). Deliberately narrow: `delete_directory`,
102
+ * `kubectl_delete`, `delete_object`, or any delete-verb tool that merely carries
103
+ * a `path` argument is NOT the `file` kind — those keep matching typed DELETE
104
+ * rules exactly as before, so an argument can never talk an action out of a
105
+ * destructive-scope rule.
106
+ */
107
+ const FILE_REMOVAL_TOOL_RE = /(?:^|_)(?:delete|remove|rm|unlink)_?(?:file|path)s?(?:$|_)|^unlink$/i;
99
108
  /** Does this rule need a content fact to fire? (A rule with no content constraint is about the ACTION, not the content.) */
100
109
  function ruleNeedsContentFact(rule) {
101
110
  return (rule.constraints || []).some(c => exports.CONTENT_FACT_FIELDS.has(c.field));
@@ -1668,6 +1677,19 @@ function inferToolContext(toolName, args) {
1668
1677
  if (args.amount !== undefined || args.price !== undefined || args.total !== undefined) {
1669
1678
  context.amount = String(args.amount ?? args.price ?? args.total);
1670
1679
  }
1680
+ // WHAT KIND of action this is — the fact a typed rule means. One verb spans
1681
+ // four kinds: DELETE is `rm -rf /` (shell destructive scope), `DELETE FROM`
1682
+ // (SQL), `DELETE /api/x` (HTTP) and `delete_file` (one file). Policies are not
1683
+ // filtered by resourceType (an "ANY + containsSecret" rule filed under
1684
+ // `database` must still catch a token leaving through Slack), so a rule that
1685
+ // means only one kind — "Block destructive SHELL commands" — constrains on
1686
+ // this fact. Without it, cleaning up one temp file in the IDE was paused as a
1687
+ // destructive shell command. Derived from the branches above, never guessed.
1688
+ context['action.kind'] = isCodeExecTool || toolNameLower === 'shell' ? 'shell'
1689
+ : context.query !== undefined && operation !== toolName && /^(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|TRUNCATE|GRANT|REVOKE|SHOW|DESCRIBE|EXPLAIN)$/.test(operation) ? 'database'
1690
+ : ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'].includes(methodArg) ? 'http'
1691
+ : context.path !== undefined && (operation === 'read' || operation === 'write' || LOCAL_FILE_ACTION_RE.test(toolName) || FILE_REMOVAL_TOOL_RE.test(toolName)) ? 'file'
1692
+ : 'tool';
1671
1693
  return { operation, context };
1672
1694
  }
1673
1695
  function inferInventoryToolOperations(toolName, toolActions = []) {
@@ -203,6 +203,17 @@ const BUILTIN_WRITE_TOOLS = new Set([
203
203
  'write', 'edit', 'multiedit', 'notebookedit', 'createfile', 'editfiles', 'writefile', 'fswrite', 'replace',
204
204
  'replacestringinfile', 'editfile', 'applypatch', 'strreplaceeditor', 'insertedittofile', 'strreplace', 'editnotebook',
205
205
  ]);
206
+ /**
207
+ * Built-ins that remove ONE file (Cursor `Delete`, Copilot `deleteFile`, …).
208
+ * Left unmapped they fell through as an unknown MCP tool named "Delete" — no
209
+ * path fact, operation = the tool name — and were judged as if a destructive
210
+ * shell command / SQL DELETE had been issued ("Block destructive shell
211
+ * commands" paused the clean-up of one temp file). Mapped, they are a FILE
212
+ * action: `delete_file {path}` → operation `delete`, `action.kind = file`,
213
+ * which the machine role ("Deletes pause for approval") and filesystem
214
+ * policies govern, and which typed DELETE rules exclude.
215
+ */
216
+ const BUILTIN_DELETE_TOOLS = new Set(['delete', 'deletefile', 'removefile', 'fsdelete', 'rmfile', 'unlink', 'deletefiles']);
206
217
  /**
207
218
  * Built-ins that never touch a file, a command, a server or the outside world —
208
219
  * nothing to enforce. Web fetch / web search are deliberately NOT here: they
@@ -249,6 +260,11 @@ function mapBuiltinToolPayload(toolName, input, payload) {
249
260
  const content = (0, fileWriteCanon_1.newContentOnly)(filePath || undefined, delta || whole);
250
261
  return { event: 'file', payload: { ...payload, file_path: filePath, content } };
251
262
  }
263
+ if (BUILTIN_DELETE_TOOLS.has(canon)) {
264
+ const filePath = str(input.file_path) || str(input.path) || str(input.filePath) || str(input.target_file) || str(input.absolute_path)
265
+ || (Array.isArray(input.files) ? input.files.map((f) => str(f)).filter(Boolean).join(', ') : '');
266
+ return { event: 'file', payload: { ...payload, file_path: filePath, fcd_file_op: 'delete' } };
267
+ }
252
268
  if (BUILTIN_PASSIVE_TOOLS.has(canon))
253
269
  return { event: 'ignore', payload };
254
270
  return null;
@@ -488,6 +504,14 @@ function buildToolCall(event, p) {
488
504
  }
489
505
  case 'file': {
490
506
  const filePath = str(p.file_path) || str(p.path);
507
+ // A built-in single-file removal (mapBuiltinToolPayload): no content to
508
+ // judge; the engine sees `delete_file {path}` → operation `delete`,
509
+ // `action.kind = file`.
510
+ if (str(p.fcd_file_op) === 'delete') {
511
+ if (!filePath)
512
+ return null;
513
+ return { toolName: 'delete_file', toolArgs: { path: filePath } };
514
+ }
491
515
  const edits = p.edits;
492
516
  const rawContent = Array.isArray(edits)
493
517
  ? edits.map((e) => str(e.new_text) || str(e.text) || str(e.content)).filter(Boolean).join('\n')
package/dist/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "1.34.7"
2
+ "version": "1.34.8"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fullcourtdefense-cli",
3
- "version": "1.34.7",
3
+ "version": "1.34.8",
4
4
  "description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
5
5
  "main": "dist/index.js",
6
6
  "bin": {