v1.2.0: guard allows computation-only inline one-liners; blocks --eval/--input-type, child_process, string-keyed access, stdin-piped scripts, deno/bun/php

This commit is contained in:
Agent committed 2026-10-11 00:54:18 +00:00
1 parent 583aa9a10b
commit bcb7468686
3 files changed
+199 -2

No files matched your search

+78
View File
@@ -0,0 +1,78 @@
// Guard rules for shell commands, focused on inline interpreter scripts.
import test from 'node:test'
import assert from 'node:assert/strict'
import { shellViolation, inlineScriptViolation } from '../index.js'
const ALLOW = [
// harmless computation one-liners
`node -e "console.log(1+1)"`,
`node -p "2**10"`,
`node --eval 'console.log([1,2,3].map(x => x * 2))'`,
`node --print="JSON.stringify({a: 1})"`,
`node -pe "Math.max(3, 7)"`,
`python3 -c "print(2**10)"`,
`python3 -c 'print(sum(range(100)))'`,
`ruby -e 'puts [1,2,3].sum'`,
`perl -e 'print 6*7, "\\n"'`,
`node -e 'console.log(JSON.parse(\`{"a":1}\`).a)'`,
// running files / tools is a normal command, not an inline script
'node --test test/',
'node build.mjs',
'node --check index.js',
'python3 manage.py migrate',
'node --version',
'python3 --version',
// unrelated commands
'git status',
'npm test 2>&1 | tail -5',
]
const BLOCK = [
// the reported gap
`node --input-type=module --eval "import fs from 'node:fs'; fs.readFileSync('a')"`,
`node --input-type=module -e "import('node:fs').then(m => m.writeFileSync('a', 'b'))"`,
// direct file / process access
`node -e "require('fs').writeFileSync('a', 'b')"`,
`node -e "require('child_process').execSync('cat f')"`,
`node -p "process.mainModule.require('fs')"`,
`node -e "fs['write'+'File']('a','b')"`,
`node -e "globalThis['req'+'uire']('fs')"`,
`node -e "this.constructor.constructor('return process')()"`,
`node -e "eval('requ'+'ire')('fs')"`,
`node -e "new Function('return process')()"`,
`node -e "fetch('http://x')"`,
`python3 -c "open('f','w').write('x')"`,
`python3 -c "import os; os.remove('f')"`,
`python3 -c "__import__('os').system('ls')"`,
`python3 -c "print(().__class__.__bases__[0].__subclasses__())"`,
`ruby -e "File.write('x', 1)"`,
'ruby -e "puts `ls`"',
`perl -e 'open(F, ">x")'`,
`deno eval "Deno.writeTextFileSync('x','y')"`,
`bun -e "Bun.write('x','y')"`,
`php -r "file_put_contents('x','y');"`,
// scripts we cannot inspect
'echo "require(1)" | node',
'cat script.js | node -',
'node - < script.js',
'python3 - < s.py',
// wrapped forms
`env FOO=1 node -e "require('fs')"`,
`sudo node -e "require('fs')"`,
`/usr/bin/node -e "require('fs')"`,
`ls && node -e "require('fs')"`,
]
test('harmless inline one-liners and normal commands are allowed', () => {
for (const c of ALLOW) assert.equal(shellViolation(c), null, `should allow: ${c}`)
})
test('file/process/network-capable or uninspectable inline scripts are blocked', () => {
for (const c of BLOCK) assert.ok(inlineScriptViolation(c) || shellViolation(c), `should block: ${c}`)
})
test('denial message points at the right tools', () => {
const m = shellViolation(`node -e "require('fs').readFileSync('a')"`)
assert.match(m, /read\/grep\/edit/)
assert.match(m, /computation-only one-liners are fine/)
})