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.
- package/dist/actionPolicyEngine.js +22 -0
- package/dist/commands/hook.js +24 -0
- package/dist/version.json +1 -1
- package/package.json +1 -1
|
@@ -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 = []) {
|
package/dist/commands/hook.js
CHANGED
|
@@ -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