@seanmozeik/tripwire 0.5.3 → 0.6.0
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/tripwire-cli.js +2 -2
- package/dist/tripwire-cli.js.jsc +0 -0
- package/dist/tripwire.js +70 -70
- package/dist/tripwire.js.jsc +0 -0
- package/package.json +5 -5
- package/src/cli.ts +2 -1
- package/src/lib/bash.ts +257 -27
- package/src/lib/log.ts +1 -0
- package/src/lib/secrets.ts +1 -0
- package/src/rules/bash-deny.ts +16 -9
- package/src/rules/bash-git.ts +4 -4
- package/src/rules/bash-network-install.ts +1 -1
- package/src/rules/bash-redirect.ts +7 -7
- package/src/rules/bash-scoped-rm.ts +1 -1
- package/src/rules/bash-tar-explosion.ts +1 -1
- package/src/rules/bash-tool-policy.ts +8 -8
- package/src/rules/lazy-code.ts +4 -4
- package/src/rules/path-protect.ts +18 -9
- package/src/rules/read-protect.ts +13 -8
|
@@ -2,10 +2,10 @@ import { type Segment, hasBypass } from '../lib/bash';
|
|
|
2
2
|
import { type Decision, allow, deny, warn } from '../lib/decision';
|
|
3
3
|
|
|
4
4
|
// Opinionated tooling enforcement. Hard-deny on the package managers and
|
|
5
|
-
// Tools
|
|
5
|
+
// Tools the user has explicitly replaced (npm/pip/patch-package); soft-warn
|
|
6
6
|
// Suggesting modern equivalents (find→fd, grep→rg).
|
|
7
7
|
//
|
|
8
|
-
// Hard-deny rationale (from
|
|
8
|
+
// Hard-deny rationale (from the user's CLAUDE.md): TypeScript is bun-only,
|
|
9
9
|
// Python is uv-only. Slipping into npm or pip mid-session means the
|
|
10
10
|
// Agent forgot the toolchain and is about to install into the wrong
|
|
11
11
|
// Directory or make a lockfile bun can't read.
|
|
@@ -23,7 +23,7 @@ const POLICIES: readonly Policy[] = [
|
|
|
23
23
|
rule: 'use-bun-not-npm',
|
|
24
24
|
action: 'deny',
|
|
25
25
|
message:
|
|
26
|
-
'Use `bun` instead of `npm`. Translations: `npm install` → `bun install`, `npm install <pkg>` → `bun add <pkg>`, `npm install -D <pkg>` → `bun add -d <pkg>`, `npm run X` → `bun run X` (or `bun X` for bin scripts), `npm test` → `bun test`. If you genuinely need npm (publishing to a registry that requires it, working in a
|
|
26
|
+
'Use `bun` instead of `npm`. Translations: `npm install` → `bun install`, `npm install <pkg>` → `bun add <pkg>`, `npm install -D <pkg>` → `bun add -d <pkg>`, `npm run X` → `bun run X` (or `bun X` for bin scripts), `npm test` → `bun test`. If you genuinely need npm (publishing to a registry that requires it, working in a different repo), append ` # tripwire-allow: <reason>` to the command.',
|
|
27
27
|
fires: (seg) => seg.head === 'npm',
|
|
28
28
|
},
|
|
29
29
|
{
|
|
@@ -35,20 +35,20 @@ const POLICIES: readonly Policy[] = [
|
|
|
35
35
|
{
|
|
36
36
|
rule: 'use-bun-not-pnpm',
|
|
37
37
|
action: 'deny',
|
|
38
|
-
message: 'Use `bun` instead of `pnpm`.
|
|
38
|
+
message: 'Use `bun` instead of `pnpm`. The user is bun-only across their repos.',
|
|
39
39
|
fires: (seg) => seg.head === 'pnpm',
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
42
|
rule: 'use-bun-not-yarn',
|
|
43
43
|
action: 'deny',
|
|
44
|
-
message: 'Use `bun` instead of `yarn`.
|
|
44
|
+
message: 'Use `bun` instead of `yarn`. The user is bun-only.',
|
|
45
45
|
fires: (seg) => seg.head === 'yarn',
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
rule: 'use-uv-not-pip',
|
|
49
49
|
action: 'deny',
|
|
50
50
|
message:
|
|
51
|
-
'Use `uv` instead of `pip`. Translations: `pip install <pkg>` → `uv add <pkg>` (project dependency) or `uv pip install <pkg>` (env-only escape hatch). `pip freeze` → `uv pip freeze`. `pip list` → `uv pip list`.
|
|
51
|
+
'Use `uv` instead of `pip`. Translations: `pip install <pkg>` → `uv add <pkg>` (project dependency) or `uv pip install <pkg>` (env-only escape hatch). `pip freeze` → `uv pip freeze`. `pip list` → `uv pip list`. The user is uv-only across Python repos.',
|
|
52
52
|
fires: (seg) => seg.head === 'pip' || seg.head === 'pip3',
|
|
53
53
|
},
|
|
54
54
|
{
|
|
@@ -76,12 +76,12 @@ const POLICIES: readonly Policy[] = [
|
|
|
76
76
|
fires: (seg) => seg.head === 'patch-package',
|
|
77
77
|
},
|
|
78
78
|
|
|
79
|
-
// ── SOFT WARNS: modern equivalents
|
|
79
|
+
// ── SOFT WARNS: modern equivalents the user has installed ────────────────
|
|
80
80
|
{
|
|
81
81
|
rule: 'consider-fd',
|
|
82
82
|
action: 'warn',
|
|
83
83
|
message:
|
|
84
|
-
'Consider `fd` instead of `find`. Faster, simpler syntax, respects .gitignore by default. Examples: `find . -name "*.ts"` → `fd -e ts`, `find . -type f -name "X"` → `fd -t f X`, `find PATH ...` → `fd ... PATH`.
|
|
84
|
+
'Consider `fd` instead of `find`. Faster, simpler syntax, respects .gitignore by default. Examples: `find . -name "*.ts"` → `fd -e ts`, `find . -type f -name "X"` → `fd -t f X`, `find PATH ...` → `fd ... PATH`. The user has both installed; either works.',
|
|
85
85
|
fires: (seg) => seg.head === 'find',
|
|
86
86
|
},
|
|
87
87
|
{
|
package/src/rules/lazy-code.ts
CHANGED
|
@@ -20,21 +20,21 @@ const STUB_RE: readonly RegExp[] = [
|
|
|
20
20
|
/\btemp fix\b/i,
|
|
21
21
|
/\bfallback\b/i,
|
|
22
22
|
/\bplaceholder\b/i,
|
|
23
|
-
/\bbackwards?[ -]?compat(ibility)?\b/i,
|
|
23
|
+
/\bbackwards?[ -]?compat(?<ibility>ibility)?\b/i,
|
|
24
24
|
/\bfor later\b/i,
|
|
25
25
|
/\blater on\b/i,
|
|
26
26
|
/\bget back to\b/i,
|
|
27
27
|
/\bI'?ll fix\b/i,
|
|
28
28
|
/\bto be implemented\b/i,
|
|
29
|
-
/\bnot yet (implemented|done)\b/i,
|
|
29
|
+
/\bnot yet (?<state>implemented|done)\b/i,
|
|
30
30
|
/\bstubbed\b/i,
|
|
31
31
|
];
|
|
32
32
|
|
|
33
33
|
const CODE_EXT_RE =
|
|
34
|
-
/\.(ts|tsx|js|jsx|mjs|cjs|py|rs|go|rb|java|kt|swift|c|cc|cpp|h|hpp|cs|php|sh|zsh|bash|lua|ex|exs|clj|scala|dart)$/i;
|
|
34
|
+
/\.(?<ext>ts|tsx|js|jsx|mjs|cjs|py|rs|go|rb|java|kt|swift|c|cc|cpp|h|hpp|cs|php|sh|zsh|bash|lua|ex|exs|clj|scala|dart)$/i;
|
|
35
35
|
|
|
36
36
|
const TEST_PATH_RE =
|
|
37
|
-
/(
|
|
37
|
+
/(?<prefix>^|\/)(?<dir>__tests__|tests?|spec|fixtures?|mocks?|__mocks__|stories)(?<suffix>\/|$)|\.(?<ext>test|spec|fixture|mock|stories)\.[^/]+$/i;
|
|
38
38
|
|
|
39
39
|
// Comment-syntax-agnostic. Works in `//`, `#`, `--`, `/* */`, `<!-- -->`,
|
|
40
40
|
// `;`, `%`, etc.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// oxlint-disable-next-line unicorn/import-style
|
|
1
2
|
import { resolve } from 'node:path';
|
|
2
3
|
|
|
3
4
|
import { type Decision, allow, deny } from '../lib/decision';
|
|
@@ -11,39 +12,47 @@ interface Spec {
|
|
|
11
12
|
|
|
12
13
|
const protections: readonly Spec[] = [
|
|
13
14
|
{
|
|
14
|
-
pattern: /(
|
|
15
|
+
pattern: /(?<prefix>^|\/)\.env(?<ext>\.[^/]+)?$/,
|
|
15
16
|
rule: 'env-file',
|
|
16
17
|
message:
|
|
17
18
|
'.env files hold secrets that should never be sent to the model. Refuse to write or edit. If an example is needed, create .env.example with redacted placeholders.',
|
|
18
19
|
},
|
|
19
20
|
{
|
|
20
|
-
pattern: /(
|
|
21
|
+
pattern: /(?<prefix>^|\/)\.dev\.vars(?<ext>\.[^/]+)?$/,
|
|
21
22
|
rule: 'dev-vars',
|
|
22
23
|
message: '.dev.vars holds Cloudflare/Wrangler secrets. Do not modify.',
|
|
23
24
|
},
|
|
24
|
-
{ pattern: /(^|\/)\.ssh\//, rule: 'ssh-dir', message: 'Never write into ~/.ssh/. Refuse.' },
|
|
25
25
|
{
|
|
26
|
-
pattern: /(
|
|
26
|
+
pattern: /(?<prefix>^|\/)\.ssh\//,
|
|
27
|
+
rule: 'ssh-dir',
|
|
28
|
+
message: 'Never write into ~/.ssh/. Refuse.',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
pattern: /(?<prefix>^|\/)(?<key>id_rsa|id_ed25519|id_ecdsa|id_dsa)(?<pub>\.pub)?$/,
|
|
27
32
|
rule: 'ssh-key',
|
|
28
33
|
message: 'SSH key file. Refuse.',
|
|
29
34
|
},
|
|
30
35
|
{
|
|
31
|
-
pattern: /\.(pem|key|p12|pfx)$/i,
|
|
36
|
+
pattern: /\.(?<ext>pem|key|p12|pfx)$/i,
|
|
32
37
|
rule: 'private-key',
|
|
33
38
|
message:
|
|
34
|
-
'Private key file. Refuse to overwrite. If generating a new key, use a different filename and let
|
|
39
|
+
'Private key file. Refuse to overwrite. If generating a new key, use a different filename and let the user review.',
|
|
35
40
|
},
|
|
36
41
|
{
|
|
37
|
-
pattern: /(
|
|
42
|
+
pattern: /(?<prefix>^|\/)secrets?\.(?<ext>json|ya?ml|toml|env)$/i,
|
|
38
43
|
rule: 'secrets-file',
|
|
39
44
|
message: 'Secrets file. Refuse.',
|
|
40
45
|
},
|
|
41
46
|
{
|
|
42
|
-
pattern: /(
|
|
47
|
+
pattern: /(?<prefix>^|\/)\.aws\/credentials$/,
|
|
43
48
|
rule: 'aws-credentials',
|
|
44
49
|
message: 'AWS credentials file. Refuse.',
|
|
45
50
|
},
|
|
46
|
-
{
|
|
51
|
+
{
|
|
52
|
+
pattern: /(?<prefix>^|\/)\.netrc$/,
|
|
53
|
+
rule: 'netrc',
|
|
54
|
+
message: '.netrc holds host credentials. Refuse.',
|
|
55
|
+
},
|
|
47
56
|
];
|
|
48
57
|
|
|
49
58
|
const pathProtect = (input: EditInput | WriteInput): Decision => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// oxlint-disable-next-line unicorn/import-style
|
|
1
2
|
import { resolve } from 'node:path';
|
|
2
3
|
|
|
3
4
|
import { type Decision, allow, deny } from '../lib/decision';
|
|
@@ -12,39 +13,43 @@ interface Spec {
|
|
|
12
13
|
const PROTECTIONS: readonly Spec[] = [
|
|
13
14
|
{
|
|
14
15
|
rule: 'read-env',
|
|
15
|
-
pattern: /(
|
|
16
|
+
pattern: /(?<prefix>^|\/)\.env(?<ext>\.[^/]+)?$/,
|
|
16
17
|
message:
|
|
17
18
|
'.env files hold secrets that should never enter the model context. Refuse to read. If the goal is documenting required env vars, look at .env.example or describe the schema from memory.',
|
|
18
19
|
},
|
|
19
20
|
{
|
|
20
21
|
rule: 'read-dev-vars',
|
|
21
|
-
pattern: /(
|
|
22
|
+
pattern: /(?<prefix>^|\/)\.dev\.vars(?<ext>\.[^/]+)?$/,
|
|
22
23
|
message: 'Refuse to read .dev.vars (Cloudflare/Wrangler secrets).',
|
|
23
24
|
},
|
|
24
|
-
{
|
|
25
|
+
{
|
|
26
|
+
rule: 'read-ssh',
|
|
27
|
+
pattern: /(?<prefix>^|\/)\.ssh\//,
|
|
28
|
+
message: 'Refuse to read files inside ~/.ssh/.',
|
|
29
|
+
},
|
|
25
30
|
{
|
|
26
31
|
rule: 'read-ssh-key',
|
|
27
|
-
pattern: /(
|
|
32
|
+
pattern: /(?<prefix>^|\/)(?<key>id_rsa|id_ed25519|id_ecdsa|id_dsa)$/,
|
|
28
33
|
message: 'Refuse to read SSH private key files.',
|
|
29
34
|
},
|
|
30
35
|
{
|
|
31
36
|
rule: 'read-private-key',
|
|
32
|
-
pattern: /\.(pem|key|p12|pfx)$/i,
|
|
37
|
+
pattern: /\.(?<ext>pem|key|p12|pfx)$/i,
|
|
33
38
|
message: 'Refuse to read private-key-shaped files.',
|
|
34
39
|
},
|
|
35
40
|
{
|
|
36
41
|
rule: 'read-aws-credentials',
|
|
37
|
-
pattern: /(
|
|
42
|
+
pattern: /(?<prefix>^|\/)\.aws\/credentials$/,
|
|
38
43
|
message: 'Refuse to read ~/.aws/credentials.',
|
|
39
44
|
},
|
|
40
45
|
{
|
|
41
46
|
rule: 'read-netrc',
|
|
42
|
-
pattern: /(
|
|
47
|
+
pattern: /(?<prefix>^|\/)\.netrc$/,
|
|
43
48
|
message: 'Refuse to read ~/.netrc (host credentials).',
|
|
44
49
|
},
|
|
45
50
|
{
|
|
46
51
|
rule: 'read-secrets-file',
|
|
47
|
-
pattern: /(
|
|
52
|
+
pattern: /(?<prefix>^|\/)secrets?\.(?<ext>json|ya?ml|toml|env)$/i,
|
|
48
53
|
message: 'Refuse to read a file named secrets.{json,yaml,toml,env}.',
|
|
49
54
|
},
|
|
50
55
|
];
|