@seanmozeik/tripwire 0.4.0 → 0.5.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 +1 -1
- package/dist/tripwire-cli.js.jsc +0 -0
- package/dist/tripwire.js +57 -53
- package/dist/tripwire.js.jsc +0 -0
- package/package.json +6 -6
- package/src/lib/bash.ts +572 -29
- package/src/rules/bash-deny.ts +47 -6
- package/src/rules/bash-git.ts +6 -4
package/src/rules/bash-deny.ts
CHANGED
|
@@ -40,6 +40,13 @@ const SPECS: readonly Spec[] = [
|
|
|
40
40
|
message: 'Fork bomb pattern detected. Refuse.',
|
|
41
41
|
match: (_seg, raw) => /:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;/.test(raw),
|
|
42
42
|
},
|
|
43
|
+
{
|
|
44
|
+
rule: 'source-script',
|
|
45
|
+
action: 'deny',
|
|
46
|
+
message:
|
|
47
|
+
'`source` / `.` executes a file in the current shell. Refuse arbitrary sourced scripts.',
|
|
48
|
+
match: (seg) => (seg.head === 'source' || seg.head === '.') && seg.tokens.length > 1,
|
|
49
|
+
},
|
|
43
50
|
{
|
|
44
51
|
rule: 'dd-raw-device',
|
|
45
52
|
action: 'deny',
|
|
@@ -329,18 +336,52 @@ const SPECS: readonly Spec[] = [
|
|
|
329
336
|
},
|
|
330
337
|
];
|
|
331
338
|
|
|
339
|
+
// Rules in this set ignore `# tripwire-allow` on the command line. They
|
|
340
|
+
// Are catastrophic / irreversible operations and hard policy rules that
|
|
341
|
+
// Have no legitimate prompt-line override. If the user genuinely needs
|
|
342
|
+
// One of these to run, they should do it in a terminal themselves.
|
|
343
|
+
const UNBYPASSABLE_RULES: ReadonlySet<string> = new Set([
|
|
344
|
+
// Catastrophic / irreversible
|
|
345
|
+
'rm-rf-root',
|
|
346
|
+
'rm-rf-home',
|
|
347
|
+
'fork-bomb',
|
|
348
|
+
'dd-raw-device',
|
|
349
|
+
'mkfs',
|
|
350
|
+
'kill-all',
|
|
351
|
+
'diskutil-destructive',
|
|
352
|
+
'tmutil-destructive',
|
|
353
|
+
// System control
|
|
354
|
+
'shutdown',
|
|
355
|
+
'csrutil',
|
|
356
|
+
'nvram',
|
|
357
|
+
'kextload',
|
|
358
|
+
'spctl-disable',
|
|
359
|
+
'xattr-quarantine-bypass',
|
|
360
|
+
'topgrade',
|
|
361
|
+
'softwareupdate-install',
|
|
362
|
+
'systemsetup',
|
|
363
|
+
'scutil-set',
|
|
364
|
+
'security-keychain-destructive',
|
|
365
|
+
// Hard policy rules
|
|
366
|
+
'no-verify',
|
|
367
|
+
'no-gpg-sign',
|
|
368
|
+
]);
|
|
369
|
+
|
|
332
370
|
const bashDeny = (segments: readonly Segment[], cmd: string): Decision => {
|
|
333
|
-
|
|
334
|
-
return allow('bash-deny');
|
|
335
|
-
}
|
|
371
|
+
const bypass = hasBypass(cmd);
|
|
336
372
|
for (const seg of segments) {
|
|
337
373
|
for (const s of SPECS) {
|
|
338
|
-
if (s.match(seg, cmd)) {
|
|
339
|
-
|
|
374
|
+
if (!s.match(seg, cmd)) {
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
if (bypass && !UNBYPASSABLE_RULES.has(s.rule)) {
|
|
378
|
+
// Caller asserted in-turn approval; honor it for this rule.
|
|
379
|
+
continue;
|
|
340
380
|
}
|
|
381
|
+
return s.action === 'deny' ? deny(s.rule, s.message) : ask(s.rule, s.message);
|
|
341
382
|
}
|
|
342
383
|
}
|
|
343
384
|
return allow('bash-deny');
|
|
344
385
|
};
|
|
345
386
|
|
|
346
|
-
export { bashDeny };
|
|
387
|
+
export { bashDeny, UNBYPASSABLE_RULES };
|
package/src/rules/bash-git.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Segment, hasBypass } from '../lib/bash';
|
|
1
|
+
import { type Segment, hasBypass, unwrapStaticString } from '../lib/bash';
|
|
2
2
|
import type { GitConfig } from '../lib/config';
|
|
3
3
|
import { type Decision, allow, ask, deny, warn } from '../lib/decision';
|
|
4
4
|
|
|
@@ -111,15 +111,17 @@ const messageOf = (subArgs: readonly string[]): string | null => {
|
|
|
111
111
|
for (let i = 0; i < subArgs.length; i++) {
|
|
112
112
|
const t = subArgs[i]!;
|
|
113
113
|
if (t === '-m' || t === '--message') {
|
|
114
|
-
|
|
114
|
+
const raw = subArgs[i + 1];
|
|
115
|
+
return raw === undefined ? null : unwrapStaticString(raw);
|
|
115
116
|
}
|
|
116
117
|
if (t.startsWith('--message=')) {
|
|
117
|
-
return t.slice('--message='.length);
|
|
118
|
+
return unwrapStaticString(t.slice('--message='.length));
|
|
118
119
|
}
|
|
119
120
|
// Combined short flags like `-am`, `-ma`, `-amS` carry the message
|
|
120
121
|
// In the next positional arg — same as `-m` alone.
|
|
121
122
|
if (/^-[a-zA-Z]*m[a-zA-Z]*$/.test(t)) {
|
|
122
|
-
|
|
123
|
+
const raw = subArgs[i + 1];
|
|
124
|
+
return raw === undefined ? null : unwrapStaticString(raw);
|
|
123
125
|
}
|
|
124
126
|
}
|
|
125
127
|
return null;
|