@getmarrow/mcp 3.9.101 → 3.9.103
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/README.md +63 -29
- package/dist/cli.js +64 -1005
- package/dist/cli.js.map +1 -1
- package/dist/codex-plugin-pins.d.ts +19 -0
- package/dist/codex-plugin-pins.d.ts.map +1 -0
- package/dist/codex-plugin-pins.js +43 -0
- package/dist/codex-plugin-pins.js.map +1 -0
- package/dist/codex-plugins.d.ts +34 -0
- package/dist/codex-plugins.d.ts.map +1 -0
- package/dist/codex-plugins.js +240 -0
- package/dist/codex-plugins.js.map +1 -0
- package/dist/env.d.ts +2 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +229 -39
- package/dist/env.js.map +1 -1
- package/dist/hook-contract.d.ts +41 -3
- package/dist/hook-contract.d.ts.map +1 -1
- package/dist/hook-contract.js +126 -18
- package/dist/hook-contract.js.map +1 -1
- package/dist/hook-pre-action.d.ts +48 -2
- package/dist/hook-pre-action.d.ts.map +1 -1
- package/dist/hook-pre-action.js +199 -23
- package/dist/hook-pre-action.js.map +1 -1
- package/dist/hook-tool-policy.d.ts +3 -3
- package/dist/hook-tool-policy.d.ts.map +1 -1
- package/dist/hook-tool-policy.js +6 -22
- package/dist/hook-tool-policy.js.map +1 -1
- package/dist/hook.d.ts +2 -2
- package/dist/hook.d.ts.map +1 -1
- package/dist/hook.js +13 -6
- package/dist/hook.js.map +1 -1
- package/dist/host-approval-state.d.ts +9 -0
- package/dist/host-approval-state.d.ts.map +1 -1
- package/dist/host-approval-state.js +4 -0
- package/dist/host-approval-state.js.map +1 -1
- package/dist/host-approval.d.ts +6 -0
- package/dist/host-approval.d.ts.map +1 -1
- package/dist/host-approval.js +3 -1
- package/dist/host-approval.js.map +1 -1
- package/dist/host-session.d.ts +11 -0
- package/dist/host-session.d.ts.map +1 -1
- package/dist/host-session.js +49 -0
- package/dist/host-session.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -19
- package/dist/index.js.map +1 -1
- package/dist/normalized-action.d.ts +29 -6
- package/dist/normalized-action.d.ts.map +1 -1
- package/dist/normalized-action.js +497 -103
- package/dist/normalized-action.js.map +1 -1
- package/dist/own-server-identity.d.ts +123 -0
- package/dist/own-server-identity.d.ts.map +1 -0
- package/dist/own-server-identity.js +1052 -0
- package/dist/own-server-identity.js.map +1 -0
- package/dist/own-tool.d.ts +87 -0
- package/dist/own-tool.d.ts.map +1 -0
- package/dist/own-tool.js +169 -0
- package/dist/own-tool.js.map +1 -0
- package/dist/redact.d.ts +23 -0
- package/dist/redact.d.ts.map +1 -1
- package/dist/redact.js +181 -8
- package/dist/redact.js.map +1 -1
- package/dist/seatbelt/classifier-pin.d.ts +15 -0
- package/dist/seatbelt/classifier-pin.d.ts.map +1 -0
- package/dist/seatbelt/classifier-pin.js +26 -0
- package/dist/seatbelt/classifier-pin.js.map +1 -0
- package/dist/seatbelt/guards.d.ts +10 -0
- package/dist/seatbelt/guards.d.ts.map +1 -0
- package/dist/seatbelt/guards.js +509 -0
- package/dist/seatbelt/guards.js.map +1 -0
- package/dist/seatbelt/hook.d.ts +72 -0
- package/dist/seatbelt/hook.d.ts.map +1 -0
- package/dist/seatbelt/hook.js +362 -0
- package/dist/seatbelt/hook.js.map +1 -0
- package/dist/seatbelt/longopts.d.ts +68 -0
- package/dist/seatbelt/longopts.d.ts.map +1 -0
- package/dist/seatbelt/longopts.js +377 -0
- package/dist/seatbelt/longopts.js.map +1 -0
- package/dist/seatbelt/mapper.d.ts +83 -0
- package/dist/seatbelt/mapper.d.ts.map +1 -0
- package/dist/seatbelt/mapper.js +10248 -0
- package/dist/seatbelt/mapper.js.map +1 -0
- package/dist/seatbelt/record.d.ts +94 -0
- package/dist/seatbelt/record.d.ts.map +1 -0
- package/dist/seatbelt/record.js +776 -0
- package/dist/seatbelt/record.js.map +1 -0
- package/dist/seatbelt/tables.d.ts +82 -0
- package/dist/seatbelt/tables.d.ts.map +1 -0
- package/dist/seatbelt/tables.js +359 -0
- package/dist/seatbelt/tables.js.map +1 -0
- package/dist/seatbelt-deploy.d.ts +100 -0
- package/dist/seatbelt-deploy.d.ts.map +1 -0
- package/dist/seatbelt-deploy.js +393 -0
- package/dist/seatbelt-deploy.js.map +1 -0
- package/dist/seatbelt-fs.d.ts +30 -0
- package/dist/seatbelt-fs.d.ts.map +1 -0
- package/dist/seatbelt-fs.js +94 -0
- package/dist/seatbelt-fs.js.map +1 -0
- package/dist/seatbelt-git.d.ts +212 -0
- package/dist/seatbelt-git.d.ts.map +1 -0
- package/dist/seatbelt-git.js +844 -0
- package/dist/seatbelt-git.js.map +1 -0
- package/dist/seatbelt-mode.d.ts +77 -0
- package/dist/seatbelt-mode.d.ts.map +1 -0
- package/dist/seatbelt-mode.js +496 -0
- package/dist/seatbelt-mode.js.map +1 -0
- package/dist/seatbelt-path.d.ts +123 -0
- package/dist/seatbelt-path.d.ts.map +1 -0
- package/dist/seatbelt-path.js +482 -0
- package/dist/seatbelt-path.js.map +1 -0
- package/dist/seatbelt-scripts.d.ts +109 -0
- package/dist/seatbelt-scripts.d.ts.map +1 -0
- package/dist/seatbelt-scripts.js +1060 -0
- package/dist/seatbelt-scripts.js.map +1 -0
- package/dist/tool-catalog.d.ts +6852 -0
- package/dist/tool-catalog.d.ts.map +1 -0
- package/dist/tool-catalog.js +999 -0
- package/dist/tool-catalog.js.map +1 -0
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
- package/server.json +10 -3
- package/vendor/classifier/MANIFEST.json +25 -0
- package/vendor/classifier/classifier.js +1356 -0
- package/vendor/classifier/policy-baseline.js +187 -0
- package/vendor/classifier/redact.js +59 -0
- package/vendor/classifier/shell-parse.js +455 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Built-in baseline policy (contract marrow.classifier.v1). It is the same data shape the server
|
|
4
|
+
// signs and ships in a policy bundle; a verified bundle replaces it. The baseline is deliberately
|
|
5
|
+
// conservative: it only names work that is safe to allow locally without a server round trip.
|
|
6
|
+
// Everything it does not name is "unknown" and goes to the server gate.
|
|
7
|
+
//
|
|
8
|
+
// Program rule forms:
|
|
9
|
+
// 'routine' | 'unknown' | 'risky' fixed class for any arguments
|
|
10
|
+
// { read: true } routine unless an argument is a secret path
|
|
11
|
+
// { write: true } path arguments are write targets (protected -> risky)
|
|
12
|
+
// { sub: {...}, default, skip } first non-option argument selects the class
|
|
13
|
+
// { handler: '<name>' } special handling in classifier.js
|
|
14
|
+
|
|
15
|
+
const SUB_FLAGS_WITH_VALUE = {
|
|
16
|
+
git: ['-C', '-c', '--git-dir', '--work-tree', '--namespace', '--exec-path'],
|
|
17
|
+
docker: ['-H', '--host', '--context', '-c', '--config', '-l', '--log-level'],
|
|
18
|
+
kubectl: ['-n', '--namespace', '--context', '--cluster', '--kubeconfig', '-s', '--server', '--user'],
|
|
19
|
+
npm: ['--prefix', '-C', '-w', '--workspace'],
|
|
20
|
+
pnpm: ['-C', '--dir', '--filter', '-F', '-w'],
|
|
21
|
+
yarn: ['--cwd'],
|
|
22
|
+
gh: ['-R', '--repo'],
|
|
23
|
+
cargo: ['--manifest-path', '-Z', '--config'],
|
|
24
|
+
terraform: ['-chdir'],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const BASELINE_POLICY = Object.freeze({
|
|
28
|
+
contract: 'marrow.classifier.v1',
|
|
29
|
+
version: 0,
|
|
30
|
+
source: 'builtin_baseline',
|
|
31
|
+
|
|
32
|
+
tools: {
|
|
33
|
+
read_only: [
|
|
34
|
+
'read', 'grep', 'glob', 'ls', 'websearch', 'web_search', 'todowrite', 'todoread', 'task', 'agent',
|
|
35
|
+
'exitplanmode', 'notebookread', 'bashoutput', 'killshell', 'askuserquestion', 'skill', 'toolsearch',
|
|
36
|
+
'slashcommand', 'list_dir', 'read_file', 'grep_files', 'view_image', 'update_plan', 'listmcpresourcestool',
|
|
37
|
+
'readmcpresourcetool', 'taskoutput', 'taskstop', 'enterplanmode', 'monitor',
|
|
38
|
+
],
|
|
39
|
+
edit: ['write', 'edit', 'multiedit', 'notebookedit', 'apply_patch', 'write_file', 'create_file', 'str_replace_editor', 'str_replace_based_edit_tool'],
|
|
40
|
+
shell: ['bash', 'shell', 'local_shell', 'exec_command', 'container.exec', 'run_terminal_command', 'execute_command', 'run_shell_command'],
|
|
41
|
+
fetch: ['webfetch', 'web_fetch', 'fetch'],
|
|
42
|
+
// Marrow's own MCP tools are governed server-side; the exact official namespace only.
|
|
43
|
+
marrow_mcp_prefix: 'mcp__marrow__marrow_',
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
// A mutation verb anywhere in an unknown or MCP tool name makes it risky; read verbs never
|
|
47
|
+
// override it (ADV-06: fetch_prod_secrets, query_and_drop_table, status_wipe).
|
|
48
|
+
mutation_verbs: [
|
|
49
|
+
'delete', 'drop', 'wipe', 'purge', 'destroy', 'truncate', 'terminate', 'grant', 'revoke', 'disable', 'flush',
|
|
50
|
+
'deploy', 'publish', 'release', 'merge', 'refund', 'charge', 'payout', 'transfer', 'pay', 'rotate', 'secret',
|
|
51
|
+
'secrets', 'credential', 'credentials', 'token', 'password', 'rollback', 'shutdown', 'kill', 'reset', 'remove',
|
|
52
|
+
'rm', 'overwrite', 'force', 'prod', 'production', 'send', 'email', 'invite', 'approve', 'archive', 'unpublish',
|
|
53
|
+
],
|
|
54
|
+
|
|
55
|
+
protected_branches: ['main', 'master', 'production', 'prod', 'release', 'release/*', 'releases/*', 'live', 'stable', 'trunk', 'hotfix/*', 'deploy', 'deploy/*', 'gh-pages'],
|
|
56
|
+
|
|
57
|
+
// Writing these is risky: they disable or redirect Marrow, a harness's hooks, or the shell.
|
|
58
|
+
protected_paths: [
|
|
59
|
+
'~/.marrow/**', '**/.marrow/**',
|
|
60
|
+
'~/.claude.json', '~/.claude/settings.json', '~/.claude/settings.local.json', '**/.claude/settings.json', '**/.claude/settings.local.json',
|
|
61
|
+
'**/.mcp.json', '~/.cursor/**', '**/.cursor/mcp.json', '**/.cursor/hooks.json', '~/.codex/**', '**/.codex/**',
|
|
62
|
+
'~/.gemini/**', '**/.gemini/settings.json', '**/.windsurf/**', '~/.codeium/**', '**/.clinerules/hooks/**',
|
|
63
|
+
'~/.grok/**', '**/.grok/**', '~/.hermes/**', '~/.openclaw/**',
|
|
64
|
+
'~/.config/systemd/user/**', '~/Library/LaunchAgents/**', '**/.config/environment.d/**', '**/.pam_environment',
|
|
65
|
+
// Directories on PATH: an executable written here shadows a routine program later.
|
|
66
|
+
'~/.local/bin/**', '~/bin/**', '/usr/local/bin/**', '/opt/homebrew/bin/**', '**/node_modules/.bin/**',
|
|
67
|
+
'**/.bashrc', '**/.bash_profile', '**/.bash_login', '**/.profile', '**/.zshrc', '**/.zshenv', '**/.zprofile', '**/.config/fish/**',
|
|
68
|
+
'**/.claude.json', '**/.config/systemd/user/**',
|
|
69
|
+
'/etc/**', '/usr/**', '/bin/**', '/sbin/**', '/boot/**', '/lib/**', '/var/lib/**', '/System/**',
|
|
70
|
+
'**/.git/hooks/**', '**/.git/config',
|
|
71
|
+
'**/.npmrc', '**/.gitconfig', '**/.git-credentials', '**/.ssh/**', '**/.aws/**', '**/.kube/**', '**/.docker/config.json',
|
|
72
|
+
],
|
|
73
|
+
// Reading these is risky (credential exposure).
|
|
74
|
+
secret_paths: [
|
|
75
|
+
// Home-agnostic on purpose: another user's or root's credential files are just as secret.
|
|
76
|
+
'**/.env', '**/.env.*', '**/.aws/**', '**/.ssh/**', '**/.npmrc', '**/.git-credentials', '**/.netrc', '**/.config/gh/**',
|
|
77
|
+
'**/.docker/config.json', '**/.kube/config', '**/.marrow/env', '**/.marrow/env.local', '**/.claude.json', '**/*.pem', '**/*.key',
|
|
78
|
+
'**/id_rsa', '**/id_rsa.*', '**/id_ed25519', '**/id_ed25519.*', '**/id_ecdsa', '**/credentials.json', '**/.dev.vars',
|
|
79
|
+
'**/.config/gcloud/**', '**/.azure/**', '**/.pypirc', '**/.gem/credentials', '**/.cargo/credentials*', '/etc/shadow',
|
|
80
|
+
'**/.hermes/config.yaml', '**/.openclaw/.env',
|
|
81
|
+
],
|
|
82
|
+
secret_path_exceptions: ['**/.env.example', '**/.env.sample', '**/.env.template', '**/.env.dist'],
|
|
83
|
+
// Edits here are not blocked locally but go to the server gate.
|
|
84
|
+
review_paths: ['**/.husky/**', '**/.githooks/**', '**/lefthook.yml', '**/.pre-commit-config.yaml', '**/.github/workflows/**', '**/Dockerfile', '**/wrangler.toml', '**/wrangler.json', '**/terraform/**', '**/*.tf', '**/k8s/**', '**/helm/**'],
|
|
85
|
+
|
|
86
|
+
url_mutation_pattern: '(delete|wipe|drop|destroy|purge|truncate|refund|charge|payout|transfer|dispatch|deploy|publish|admin|_method=|confirm=|execute|reset)',
|
|
87
|
+
interpreter_danger_pattern: '(rmtree|rmSync|rmdirSync|unlink|os\\.remove|os\\.system|subprocess|child_process|execSync|spawnSync|exec\\(|system\\(|File\\.delete|FileUtils\\.rm|shutil|\\.aws|\\.ssh|\\.env|credentials|id_rsa|id_ed25519|wrangler|deploy|publish|DROP |TRUNCATE|DELETE FROM|/dev/sd|mkfs|dd if=)',
|
|
88
|
+
sql_mutation_pattern: '\\b(drop|delete|truncate|update|alter|insert|grant|revoke|flushall|flushdb|replace into)\\b',
|
|
89
|
+
|
|
90
|
+
wrappers: ['env', 'timeout', 'nice', 'nohup', 'time', 'command', 'builtin', 'exec', 'stdbuf', 'ionice', 'chrt', 'taskset', 'setsid', 'unbuffer', 'caffeinate', 'xargs', 'watch', 'retry'],
|
|
91
|
+
privilege_wrappers: ['sudo', 'doas', 'su', 'pkexec', 'runas'],
|
|
92
|
+
shells: ['sh', 'bash', 'zsh', 'dash', 'ksh', 'fish', 'ash', 'busybox'],
|
|
93
|
+
interpreters: ['python', 'python2', 'python3', 'node', 'nodejs', 'ruby', 'perl', 'php', 'deno', 'bun', 'lua', 'rscript', 'osascript', 'pwsh', 'powershell', 'tclsh', 'jshell', 'groovy', 'scala', 'swift', 'irb', 'ipython'],
|
|
94
|
+
routine_python_modules: ['pytest', 'unittest', 'mypy', 'black', 'ruff', 'flake8', 'pylint', 'pip', 'venv', 'json.tool', 'doctest', 'compileall', 'py_compile', 'coverage', 'isort'],
|
|
95
|
+
routine_npx: ['tsc', 'eslint', 'prettier', 'vitest', 'jest', 'mocha', 'biome', 'c8', 'nyc', 'typescript', 'markdownlint', 'markdownlint-cli2', 'cspell', 'depcheck', 'knip', 'lint-staged', 'playwright',
|
|
96
|
+
'husky', 'vite', 'next', 'webpack', 'webpack-cli', 'rollup', 'esbuild', 'tsup', 'parcel', 'astro', 'nuxi', 'storybook'],
|
|
97
|
+
routine_package_scripts_depth: 3,
|
|
98
|
+
|
|
99
|
+
programs: {
|
|
100
|
+
// Read-only inspection.
|
|
101
|
+
ls: 'routine', ll: 'routine', dir: 'routine', pwd: 'routine', echo: 'routine', printf: 'routine', true: 'routine', false: 'routine',
|
|
102
|
+
':': 'routine', test: 'routine', '[': 'routine', '[[': 'routine', which: 'routine', type: 'routine', whereis: 'routine', whoami: 'routine',
|
|
103
|
+
id: 'routine', uname: 'routine', hostname: 'routine', date: 'routine', uptime: 'routine', sleep: 'routine', seq: 'routine',
|
|
104
|
+
basename: 'routine', dirname: 'routine', realpath: 'routine', readlink: 'routine', cd: { handler: 'cd' }, pushd: 'routine', popd: 'routine',
|
|
105
|
+
export: { handler: 'export' }, unset: 'routine', set: 'routine', shopt: 'unknown', trap: 'unknown', wait: 'routine', jobs: 'routine', read: 'routine',
|
|
106
|
+
exit: 'routine', return: 'routine', local: { handler: 'export' }, declare: { handler: 'export' }, typeset: { handler: 'export' }, readonly: { handler: 'export' }, history: 'routine', help: 'routine',
|
|
107
|
+
hash: 'unknown', enable: 'unknown',
|
|
108
|
+
ps: 'routine', top: 'routine', htop: 'routine', free: 'routine', df: 'routine', du: { read: true }, lsof: 'routine', nproc: 'routine',
|
|
109
|
+
cat: { read: true }, head: { read: true }, tail: { read: true }, less: { read: true }, more: { read: true }, wc: { read: true },
|
|
110
|
+
grep: { read: true }, egrep: { read: true }, fgrep: { read: true }, rg: { handler: 'rg' }, ag: { read: true }, ack: { read: true },
|
|
111
|
+
find: { handler: 'find' }, fd: { handler: 'fd' }, tree: { read: true }, file: { read: true }, stat: { read: true }, diff: { read: true },
|
|
112
|
+
cmp: { read: true }, comm: { read: true }, sort: { read: true }, uniq: { read: true }, cut: { read: true }, tr: 'routine', paste: { read: true },
|
|
113
|
+
column: { read: true }, nl: { read: true }, fold: { read: true }, rev: { read: true }, tac: { read: true }, jq: { read: true }, yq: { read: true },
|
|
114
|
+
xxd: { read: true }, od: { read: true }, hexdump: { read: true }, strings: { read: true }, base64: { read: true }, base32: { read: true },
|
|
115
|
+
sha256sum: { read: true }, sha1sum: { read: true }, md5sum: { read: true }, shasum: { read: true }, cksum: { read: true },
|
|
116
|
+
awk: { handler: 'awk' }, gawk: { handler: 'awk' }, mawk: { handler: 'awk' }, sed: { handler: 'sed' },
|
|
117
|
+
env: 'routine', printenv: 'routine',
|
|
118
|
+
// Local file changes inside the workspace.
|
|
119
|
+
mkdir: { write: true }, touch: { write: true }, cp: { write: true }, mv: { write: true }, ln: { write: true }, tee: { write: true },
|
|
120
|
+
install: { write: true }, truncate: { write: true }, rmdir: { write: true }, rm: { handler: 'rm' }, unlink: { write: true },
|
|
121
|
+
chmod: { handler: 'chmod' }, chown: { handler: 'chmod' }, chgrp: { handler: 'chmod' },
|
|
122
|
+
tar: { handler: 'archive' }, zip: { read: true }, unzip: { handler: 'archive' }, gzip: { read: true }, gunzip: { read: true }, xz: { read: true }, unxz: { read: true }, bzip2: { read: true }, zstd: { read: true }, '7z': 'unknown',
|
|
123
|
+
patch: 'routine',
|
|
124
|
+
// Developer tools.
|
|
125
|
+
git: { handler: 'git' },
|
|
126
|
+
gh: { handler: 'gh' },
|
|
127
|
+
npm: { handler: 'pkg' }, pnpm: { handler: 'pkg' }, yarn: { handler: 'pkg' }, bun: { handler: 'bun' },
|
|
128
|
+
npx: { handler: 'npx' }, pnpx: { handler: 'npx' }, bunx: { handler: 'npx' },
|
|
129
|
+
tsc: 'routine', eslint: 'routine', prettier: 'routine', biome: 'routine', jest: 'routine', vitest: 'routine', mocha: 'routine',
|
|
130
|
+
// Dev servers, bundlers and git-hook installers: they run project config the agent may have
|
|
131
|
+
// written, so agentd's workspace taint escalates them after process-spawning code is written.
|
|
132
|
+
husky: 'routine', vite: 'routine', next: 'routine', webpack: 'routine', 'webpack-cli': 'routine', rollup: 'routine', esbuild: 'routine',
|
|
133
|
+
tsup: 'routine', parcel: 'routine', astro: 'routine', nuxi: 'routine', nuxt: 'routine', storybook: 'routine', gatsby: 'routine', remix: 'routine',
|
|
134
|
+
pytest: 'routine', ruff: 'routine', black: 'routine', mypy: 'routine', flake8: 'routine', pylint: 'routine', isort: 'routine',
|
|
135
|
+
shellcheck: 'routine', actionlint: 'routine', yamllint: 'routine', hadolint: 'routine', golangci_lint: 'routine', 'golangci-lint': 'routine',
|
|
136
|
+
gofmt: 'routine', rustfmt: 'routine', clang_format: 'routine', 'clang-format': 'routine', cloc: 'routine', tokei: 'routine',
|
|
137
|
+
node: { handler: 'interpreter' }, nodejs: { handler: 'interpreter' },
|
|
138
|
+
cargo: { sub: { build: 'routine', test: 'routine', check: 'routine', clippy: 'routine', fmt: 'routine', doc: 'routine', tree: 'routine', metadata: 'routine', bench: 'routine', add: 'routine', update: 'routine', search: 'routine', publish: 'risky', yank: 'risky', owner: 'risky', login: 'risky', logout: 'risky' }, default: 'unknown' },
|
|
139
|
+
go: { sub: { build: 'routine', test: 'routine', vet: 'routine', fmt: 'routine', mod: 'routine', list: 'routine', version: 'routine', env: 'routine', doc: 'routine', get: 'routine' }, default: 'unknown' },
|
|
140
|
+
pip: { handler: 'pip' }, pip3: { handler: 'pip' },
|
|
141
|
+
uv: { sub: { pip: 'routine', sync: 'routine', lock: 'routine', add: 'routine', venv: 'routine', tree: 'routine', publish: 'risky' }, default: 'unknown' },
|
|
142
|
+
poetry: { sub: { install: 'routine', show: 'routine', lock: 'routine', check: 'routine', add: 'routine', build: 'routine', publish: 'risky' }, default: 'unknown' },
|
|
143
|
+
twine: { sub: { check: 'routine', upload: 'risky' }, default: 'unknown' },
|
|
144
|
+
gem: { sub: { list: 'routine', install: 'routine', build: 'routine', push: 'risky', yank: 'risky', owner: 'risky' }, default: 'unknown' },
|
|
145
|
+
mvn: { sub: { compile: 'routine', test: 'routine', package: 'routine', verify: 'routine', clean: 'routine', deploy: 'risky', release: 'risky' }, default: 'unknown' },
|
|
146
|
+
dotnet: { sub: { build: 'routine', test: 'routine', restore: 'routine', format: 'routine', nuget: 'unknown' }, default: 'unknown' },
|
|
147
|
+
docker: { handler: 'docker' }, podman: { handler: 'docker' },
|
|
148
|
+
kubectl: { sub: { get: 'routine', describe: 'routine', logs: 'routine', explain: 'routine', version: 'routine', 'api-resources': 'routine', top: 'routine', diff: 'routine', config: 'unknown', apply: 'risky', delete: 'risky', exec: 'risky', scale: 'risky', rollout: 'risky', patch: 'risky', replace: 'risky', create: 'risky', edit: 'risky', drain: 'risky', cordon: 'risky', taint: 'risky', label: 'risky', annotate: 'risky', 'port-forward': 'unknown', cp: 'risky', run: 'risky', set: 'risky', auth: 'unknown' }, default: 'unknown', skip: 'kubectl' },
|
|
149
|
+
helm: { sub: { list: 'routine', ls: 'routine', status: 'routine', template: 'routine', lint: 'routine', show: 'routine', search: 'routine', repo: 'unknown', install: 'risky', upgrade: 'risky', uninstall: 'risky', delete: 'risky', rollback: 'risky' }, default: 'unknown' },
|
|
150
|
+
terraform: { sub: { plan: 'routine', validate: 'routine', fmt: 'routine', init: 'routine', show: 'routine', output: 'routine', providers: 'routine', graph: 'routine', version: 'routine', apply: 'risky', destroy: 'risky', import: 'risky', state: 'risky', taint: 'risky', untaint: 'risky', 'force-unlock': 'risky', workspace: 'unknown', console: 'unknown' }, default: 'unknown', skip: 'terraform' },
|
|
151
|
+
tofu: { sub: { plan: 'routine', validate: 'routine', fmt: 'routine', init: 'routine', show: 'routine', apply: 'risky', destroy: 'risky', import: 'risky', state: 'risky' }, default: 'unknown' },
|
|
152
|
+
pulumi: { sub: { preview: 'routine', stack: 'unknown', up: 'risky', destroy: 'risky', refresh: 'risky', cancel: 'risky' }, default: 'unknown' },
|
|
153
|
+
wrangler: { sub: { dev: 'routine', whoami: 'routine', types: 'routine', tail: 'routine', deploy: 'risky', publish: 'risky', secret: 'risky', rollback: 'risky', delete: 'risky', d1: 'risky', kv: 'unknown', r2: 'unknown', versions: 'unknown', login: 'risky' }, default: 'unknown' },
|
|
154
|
+
fly: { sub: { status: 'routine', logs: 'routine', version: 'routine', deploy: 'risky', ssh: 'risky', secrets: 'risky', scale: 'risky', destroy: 'risky', apps: 'unknown', postgres: 'risky', machine: 'risky', console: 'risky' }, default: 'unknown' },
|
|
155
|
+
flyctl: { sub: { status: 'routine', logs: 'routine', deploy: 'risky', ssh: 'risky', secrets: 'risky', scale: 'risky', destroy: 'risky' }, default: 'unknown' },
|
|
156
|
+
vercel: 'risky', netlify: 'risky', heroku: 'risky', firebase: 'risky', railway: 'risky', serverless: 'risky', sls: 'risky', cdk: 'risky', eksctl: 'risky',
|
|
157
|
+
aws: 'risky', gcloud: 'risky', az: 'risky', doctl: 'risky', supabase: 'risky', stripe: 'risky', 'ansible-playbook': 'risky', ansible: 'risky',
|
|
158
|
+
// Databases: opaque by default; SQL mutation text makes them risky.
|
|
159
|
+
psql: { handler: 'sql' }, mysql: { handler: 'sql' }, sqlite3: { handler: 'sql' }, mongosh: { handler: 'sql' }, mongo: { handler: 'sql' },
|
|
160
|
+
'redis-cli': { handler: 'sql' }, cqlsh: { handler: 'sql' }, 'clickhouse-client': { handler: 'sql' }, pg_dump: 'unknown', pg_restore: 'risky', dropdb: 'risky', createdb: 'unknown',
|
|
161
|
+
// Network and remote execution.
|
|
162
|
+
curl: { handler: 'http' }, wget: { handler: 'http' }, http: { handler: 'http' }, https: { handler: 'http' }, xh: { handler: 'http' },
|
|
163
|
+
ssh: 'risky', scp: 'risky', sftp: 'risky', rsync: { handler: 'rsync' }, mosh: 'risky', telnet: 'risky', nc: 'risky', ncat: 'risky', socat: 'risky',
|
|
164
|
+
// System changes.
|
|
165
|
+
dd: 'risky', mkfs: 'risky', 'mkfs.ext4': 'risky', 'mkfs.xfs': 'risky', fdisk: 'risky', parted: 'risky', wipefs: 'risky', shred: 'risky',
|
|
166
|
+
shutdown: 'risky', reboot: 'risky', halt: 'risky', poweroff: 'risky', init: 'risky', iptables: 'risky', ufw: 'risky', nft: 'risky',
|
|
167
|
+
useradd: 'risky', userdel: 'risky', usermod: 'risky', passwd: 'risky', chpasswd: 'risky', visudo: 'risky', mount: 'risky', umount: 'risky',
|
|
168
|
+
swapoff: 'risky', crontab: { sub: { '-l': 'routine' }, default: 'risky', flags: true },
|
|
169
|
+
systemctl: { handler: 'systemctl' }, service: 'risky', launchctl: { sub: { list: 'routine', print: 'routine' }, default: 'risky' },
|
|
170
|
+
kill: { handler: 'kill' }, pkill: { handler: 'kill' }, killall: { handler: 'kill' },
|
|
171
|
+
// Marrow self-protection: control and uninstall are owner-only.
|
|
172
|
+
'marrow-agentd': { sub: { status: 'routine', doctor: 'routine', version: 'routine', hook: 'routine', control: 'risky', stop: 'risky', uninstall: 'risky', run: 'risky' }, default: 'risky' },
|
|
173
|
+
'marrow-install': { sub: { doctor: 'routine', status: 'routine', detect: 'routine', control: 'risky', uninstall: 'risky', agentd: 'risky' }, default: 'unknown' },
|
|
174
|
+
'marrow-mcp': 'unknown',
|
|
175
|
+
// Opaque task runners: targets can do anything (ADV-06 H6); the server decides.
|
|
176
|
+
make: 'unknown', just: 'unknown', task: 'unknown', rake: 'unknown', invoke: 'unknown', nox: 'unknown', tox: 'unknown',
|
|
177
|
+
gradle: 'unknown', gradlew: 'unknown', turbo: 'unknown', nx: 'unknown', lerna: 'unknown',
|
|
178
|
+
eval: 'unknown', source: { handler: 'source' }, '.': { handler: 'source' }, alias: 'unknown',
|
|
179
|
+
open: 'unknown', 'xdg-open': 'unknown', code: 'unknown', vim: 'unknown', nvim: 'unknown', nano: 'unknown', emacs: 'unknown',
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
// Behaviour when the server gate cannot answer. Risky actions always fail closed.
|
|
183
|
+
outage: { risky: 'deny', unknown: 'deny' },
|
|
184
|
+
sub_flags_with_value: SUB_FLAGS_WITH_VALUE,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
module.exports = { BASELINE_POLICY };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Redaction for anything the daemon sends to the server or writes to its queue or status. It
|
|
4
|
+
// removes secret-shaped values from argv and free text before they leave the process. Keys are
|
|
5
|
+
// never logged; this is a second line of defence for tool input the agent supplied.
|
|
6
|
+
//
|
|
7
|
+
// Input is truncated BEFORE any pattern runs and every quantifier is bounded, so redaction is
|
|
8
|
+
// linear in a small constant no matter how large the tool input is (a 48 KB path once took 10 s).
|
|
9
|
+
|
|
10
|
+
const SECRET_PATTERNS = [
|
|
11
|
+
/\bmrw_(?:live|test)_[A-Za-z0-9]{4,}/g,
|
|
12
|
+
/\bmrw_[A-Za-z0-9_]{8,}/g,
|
|
13
|
+
/\bmarrow_[A-Za-z0-9_]{8,}/g,
|
|
14
|
+
/\b(?:sk|pk|rk)-[A-Za-z0-9_-]{12,}/g,
|
|
15
|
+
/\bsk_(?:live|test)_[A-Za-z0-9]{8,}/g,
|
|
16
|
+
/\b(?:ghp|gho|ghu|ghs|ghr|github_pat)_[A-Za-z0-9_]{12,}/g,
|
|
17
|
+
/\bxox[abprs]-[A-Za-z0-9-]{8,}/g,
|
|
18
|
+
/\bAKIA[0-9A-Z]{12,}/g,
|
|
19
|
+
/\bAIza[0-9A-Za-z_-]{20,}/g,
|
|
20
|
+
/\bnpm_[A-Za-z0-9]{20,}/g,
|
|
21
|
+
/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{4,}/g,
|
|
22
|
+
/-----BEGIN [A-Z ]{0,40}PRIVATE KEY-----[\s\S]{0,8192}?(?:-----END [A-Z ]{0,40}PRIVATE KEY-----|$)/g,
|
|
23
|
+
/\b(?:bearer|token|basic)\s{1,4}[A-Za-z0-9._~+/=-]{12,}/gi,
|
|
24
|
+
/\b[A-Za-z0-9+/_-]{40,}={0,2}/g,
|
|
25
|
+
];
|
|
26
|
+
const ASSIGNMENT_PATTERN = /\b([A-Za-z0-9_.-]{0,32}(?:key|token|secret|password|passwd|pwd|auth|credential|cookie|session)[A-Za-z0-9_.-]{0,32})\s{0,4}([=:])\s{0,4}("[^"]{0,512}"|'[^']{0,512}'|[^\s&"']{1,512})/gi;
|
|
27
|
+
const URL_CREDENTIALS = /(\b[a-z][a-z0-9+.-]{0,20}:\/\/)[^\s/@:]{1,256}:[^\s/@]{1,256}@/gi;
|
|
28
|
+
|
|
29
|
+
function redactText(value, maxLength = 200) {
|
|
30
|
+
const raw = String(value == null ? '' : value);
|
|
31
|
+
// Keep a little more than we return so a secret cut at the boundary is still recognised.
|
|
32
|
+
let text = raw.length > maxLength * 2 + 64 ? raw.slice(0, maxLength * 2 + 64) : raw;
|
|
33
|
+
text = text.replace(URL_CREDENTIALS, '$1[redacted]@');
|
|
34
|
+
text = text.replace(ASSIGNMENT_PATTERN, (_, name, sep) => `${name}${sep}[redacted]`);
|
|
35
|
+
for (const pattern of SECRET_PATTERNS) text = text.replace(pattern, '[redacted]');
|
|
36
|
+
text = text.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, ' ');
|
|
37
|
+
return text.length > maxLength || raw.length > text.length + 64 ? `${text.slice(0, maxLength)}…` : text;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// A short label token (reason code, program name, host): redacted and restricted to a safe
|
|
41
|
+
// alphabet so tool input can never smuggle a secret through a reason string.
|
|
42
|
+
function safeToken(value, maxLength = 64) {
|
|
43
|
+
return redactText(value, maxLength).replace(/[^A-Za-z0-9._:/@+\-[\]]/g, '_');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function redactArgv(argv, { maxArgs = 32, maxLength = 160 } = {}) {
|
|
47
|
+
const out = [];
|
|
48
|
+
let hideNext = false;
|
|
49
|
+
for (const raw of (argv || []).slice(0, maxArgs)) {
|
|
50
|
+
const arg = String(raw);
|
|
51
|
+
if (hideNext) { out.push('[redacted]'); hideNext = false; continue; }
|
|
52
|
+
if (/^(?:-H|--header|-u|--user|--password|--token|--otp|--api-key|--key|--secret|-p)$/i.test(arg)) { out.push(arg); hideNext = true; continue; }
|
|
53
|
+
out.push(redactText(arg, maxLength));
|
|
54
|
+
}
|
|
55
|
+
if ((argv || []).length > maxArgs) out.push(`[+${argv.length - maxArgs} args]`);
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = { redactText, redactArgv, safeToken };
|