@seanmozeik/tripwire 0.7.2 → 0.8.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.
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  import { Effect } from 'effect';
3
+ import { type BashAnalysisOptions } from './lib/bash';
3
4
  import { type Config, type ResolvedConfig, type SecretScannerConfig } from './lib/config';
4
5
  import { type Decision } from './lib/decision';
5
6
  import { type HookEvent } from './lib/event.ts';
@@ -9,10 +10,11 @@ interface Rule {
9
10
  readonly name: string;
10
11
  readonly fn: RuleFn;
11
12
  }
12
- declare const collectPreToolUseRules: (tool: string, input: unknown, config: ResolvedConfig) => Rule[];
13
+ declare const collectPreToolUseRules: (tool: string, input: unknown, config: ResolvedConfig, cwd?: string) => Rule[];
13
14
  declare const collectPostToolUseRules: (tool: string, response: unknown, secretScanner: SecretScannerConfig) => Rule[];
14
15
  declare const runRules: (rules: readonly Rule[], timeoutMs: number) => Effect.Effect<Decision>;
15
16
  declare const runRulesSync: (rules: readonly Rule[]) => Decision;
16
17
  declare const decide: (event: HookEvent, config?: Config) => Decision;
18
+ declare const decideBash: (command: string, config?: Config, options?: BashAnalysisOptions) => Decision;
17
19
  declare const runHook: () => void;
18
- export { collectPostToolUseRules, collectPreToolUseRules, decide, normalizeToolName, runHook, runRules, runRulesSync, };
20
+ export { collectPostToolUseRules, collectPreToolUseRules, decide, decideBash, normalizeToolName, runHook, runRules, runRulesSync, };
@@ -2,5 +2,5 @@ export type { Decision } from './lib/decision';
2
2
  export type { HookEvent } from './lib/event';
3
3
  export type { Config, ConfigLoad, ResolvedConfig } from './lib/config';
4
4
  export { allow, deny, ask, warn } from './lib/decision';
5
- export { decide } from './dispatch';
5
+ export { decide, decideBash } from './dispatch';
6
6
  export { getDefaultConfig, loadConfig, loadConfigResult, mergeWithDefaults } from './lib/config';
@@ -0,0 +1,3 @@
1
+ import type { BashAnalysisOptions, ShellProgram } from './types';
2
+ declare const analyzeBash: (source: string, options?: BashAnalysisOptions) => ShellProgram;
3
+ export { analyzeBash };
@@ -0,0 +1,11 @@
1
+ import type { Command } from 'unbash';
2
+ import type { ExecutionContext, ExecutionHost } from './execution-types';
3
+ import type { ExecutionCarrierAlias, ShellInvocation } from './types';
4
+ import type { Environment } from './values';
5
+ declare class ExecutionCarriers {
6
+ #private;
7
+ constructor(host: ExecutionHost, aliases: readonly ExecutionCarrierAlias[]);
8
+ isCarrierCommand(command: Command): boolean;
9
+ inspect(invocation: ShellInvocation, environment: Environment, context: ExecutionContext): boolean;
10
+ }
11
+ export { ExecutionCarriers };
@@ -0,0 +1,17 @@
1
+ import { Schema } from 'effect';
2
+ declare const ExecutionCarrierKindSchema: Schema.Literal<"ssh">;
3
+ declare const ExecutionCarrierAliasSchema: Schema.Struct<{
4
+ readonly command: Schema.$Array<Schema.NonEmptyString>;
5
+ readonly equivalentTo: Schema.Literal<"ssh">;
6
+ }>;
7
+ declare const ShellConfigSchema: Schema.Struct<{
8
+ readonly executionCarrierAliases: Schema.optional<Schema.$Array<Schema.Struct<{
9
+ readonly command: Schema.$Array<Schema.NonEmptyString>;
10
+ readonly equivalentTo: Schema.Literal<"ssh">;
11
+ }>>>;
12
+ }>;
13
+ type ExecutionCarrierAlias = typeof ExecutionCarrierAliasSchema.Type;
14
+ type ExecutionCarrierKind = typeof ExecutionCarrierKindSchema.Type;
15
+ type ShellConfig = typeof ShellConfigSchema.Type;
16
+ export type { ExecutionCarrierAlias, ExecutionCarrierKind, ShellConfig };
17
+ export { ExecutionCarrierAliasSchema, ExecutionCarrierKindSchema, ShellConfigSchema };
@@ -0,0 +1,13 @@
1
+ import type { ShellDiagnosticKind, ShellInvocation, ShellWord, SourceRange } from './types';
2
+ import type { Environment } from './values';
3
+ interface ExecutionContext {
4
+ readonly inspectedPipelineInput: boolean;
5
+ readonly source: string;
6
+ readonly pipeline: ShellInvocation['pipeline'];
7
+ }
8
+ interface ExecutionHost {
9
+ readonly addDiagnostic: (kind: ShellDiagnosticKind, message: string, range: SourceRange) => void;
10
+ readonly emitSynthetic: (words: readonly ShellWord[], parent: ShellInvocation, environment: Environment, context: ExecutionContext) => void;
11
+ readonly inspectShellSource: (word: ShellWord | undefined, environment: Environment, context: ExecutionContext) => void;
12
+ }
13
+ export type { ExecutionContext, ExecutionHost };
@@ -0,0 +1,13 @@
1
+ import type { Node, Redirect } from 'unbash';
2
+ import type { ExecutionContext, ExecutionHost } from './execution-types';
3
+ import type { ExecutionCarrierAlias, ShellInvocation } from './types';
4
+ import { type Environment } from './values';
5
+ declare class ExecutionInspector {
6
+ #private;
7
+ constructor(host: ExecutionHost, carrierAliases: readonly ExecutionCarrierAlias[]);
8
+ inspectShellInput(invocation: ShellInvocation, redirects: readonly Redirect[], environment: Environment, context: ExecutionContext): boolean;
9
+ inspectWrapper(invocation: ShellInvocation, environment: Environment, context: ExecutionContext, inlineShellSource?: boolean): void;
10
+ inspectPipelineInputs(commands: readonly Node[], environment: Environment, context: ExecutionContext): ReadonlySet<number>;
11
+ }
12
+ export { ExecutionInspector };
13
+ export type { ExecutionContext } from './execution-types';
@@ -0,0 +1,8 @@
1
+ import type { ShellProgram } from './types';
2
+ declare const isSafePathTarget: (raw: string, extraRelative?: readonly string[], extraAbsolute?: readonly string[]) => boolean;
3
+ declare const safeScopesSummary: (extraRelative?: readonly string[], extraAbsolute?: readonly string[]) => string;
4
+ declare const hasBypass: (program: ShellProgram) => boolean;
5
+ export type { BashAnalysisOptions, ExecutionCarrierAlias, ExecutionCarrierKind, PipelinePosition, ShellDiagnostic, ShellDiagnosticKind, ShellInvocation, ShellProgram, ShellRedirect, ShellWord, ShellWordKind, SourceRange, } from './types';
6
+ export { analyzeBash } from './analyze';
7
+ export { DYNAMIC_VALUE } from './values';
8
+ export { hasBypass, isSafePathTarget, safeScopesSummary };
@@ -0,0 +1,10 @@
1
+ import type { Node } from 'unbash';
2
+ import type { ExecutionCarriers } from './carriers';
3
+ import type { ExecutionContext, ExecutionHost } from './execution-types';
4
+ import type { Environment } from './values';
5
+ declare class PipelineInputInspector {
6
+ #private;
7
+ constructor(host: ExecutionHost, carriers: ExecutionCarriers);
8
+ inspect(commands: readonly Node[], environment: Environment, context: ExecutionContext): ReadonlySet<number>;
9
+ }
10
+ export { PipelineInputInspector };
@@ -0,0 +1,9 @@
1
+ import type { Command } from 'unbash';
2
+ import type { ShellWord, SourceRange } from './types';
3
+ declare const rangeOf: (node: {
4
+ readonly pos: number;
5
+ readonly end: number;
6
+ }) => SourceRange;
7
+ declare const basename: (value: string) => string;
8
+ declare const staticCommandWords: (command: Command) => ShellWord[] | null;
9
+ export { basename, rangeOf, staticCommandWords };
@@ -0,0 +1,58 @@
1
+ import type { ExecutionCarrierAlias } from './config';
2
+ interface SourceRange {
3
+ readonly start: number;
4
+ readonly end: number;
5
+ }
6
+ type ShellWordKind = 'literal' | 'dynamic' | 'trusted-temp-path' | 'background-pid';
7
+ interface BashAnalysisOptions {
8
+ readonly cwd?: string;
9
+ readonly executionCarrierAliases?: readonly ExecutionCarrierAlias[];
10
+ readonly positionalArguments?: readonly string[];
11
+ }
12
+ interface ShellWord {
13
+ /** Raw shell source for this word. */
14
+ readonly source: string;
15
+ /** Dequoted policy value. Dynamic values use a fail-closed sentinel. */
16
+ readonly value: string;
17
+ readonly kind: ShellWordKind;
18
+ readonly range: SourceRange;
19
+ readonly quoted: boolean;
20
+ readonly variable?: string;
21
+ }
22
+ interface ShellRedirect {
23
+ readonly op: '>' | '>>' | '<' | '<<' | '<<<' | '<>' | '>&' | '<&' | '&>' | '&>>';
24
+ readonly target: ShellWord;
25
+ readonly range: SourceRange;
26
+ }
27
+ interface PipelinePosition {
28
+ readonly id: number;
29
+ readonly index: number;
30
+ }
31
+ interface ShellInvocation {
32
+ readonly id: number;
33
+ readonly head: string;
34
+ readonly words: readonly ShellWord[];
35
+ readonly tokens: readonly string[];
36
+ readonly args: readonly string[];
37
+ readonly flags: readonly string[];
38
+ readonly redirects: readonly ShellRedirect[];
39
+ readonly raw: string;
40
+ readonly range: SourceRange;
41
+ readonly pipeline: PipelinePosition | undefined;
42
+ readonly synthetic: boolean;
43
+ }
44
+ type ShellDiagnosticKind = 'parse-error' | 'dynamic-executable' | 'dynamic-shell-source' | 'eval' | 'function' | 'alias';
45
+ interface ShellDiagnostic {
46
+ readonly kind: ShellDiagnosticKind;
47
+ readonly message: string;
48
+ readonly range: SourceRange;
49
+ }
50
+ interface ShellProgram {
51
+ readonly source: string;
52
+ readonly invocations: readonly ShellInvocation[];
53
+ readonly redirects: readonly ShellRedirect[];
54
+ readonly diagnostics: readonly ShellDiagnostic[];
55
+ readonly hasBypass: boolean;
56
+ }
57
+ export type { BashAnalysisOptions, PipelinePosition, ShellDiagnostic, ShellDiagnosticKind, ShellInvocation, ShellProgram, ShellRedirect, ShellWord, ShellWordKind, SourceRange, };
58
+ export type { ExecutionCarrierAlias, ExecutionCarrierKind } from './config';
@@ -0,0 +1,25 @@
1
+ import type { Function as BashFunction, Word, WordPart } from 'unbash';
2
+ import type { ShellWord } from './types';
3
+ declare const DYNAMIC_VALUE = "__tripwire_dynamic_shell_value__";
4
+ declare const BACKGROUND_PID_VALUE = "__tripwire_background_pid__";
5
+ interface TrustedTempValue {
6
+ readonly variable: string;
7
+ }
8
+ interface Environment {
9
+ readonly aliases: Map<string, string>;
10
+ backgroundPidAvailable: boolean;
11
+ readonly bindings: Map<string, ShellWord>;
12
+ readonly functions: Map<string, BashFunction>;
13
+ readonly temps: Map<string, TrustedTempValue>;
14
+ }
15
+ declare const emptyEnvironment: () => Environment;
16
+ declare const cloneEnvironment: (environment: Environment) => Environment;
17
+ declare const isQuotedPart: (part: WordPart) => boolean;
18
+ declare const wordIsStatic: (word: Word) => boolean;
19
+ declare const staticGeneratedValue: (word: Word, environment: Environment) => string | null;
20
+ declare const isTrustedMktemp: (word: Word, environment: Environment) => boolean;
21
+ declare const trustedTempWord: (word: Word, environment: Environment) => ShellWord | null;
22
+ declare const boundWord: (word: Word, environment: Environment) => ShellWord | null;
23
+ declare const backgroundPidWord: (word: Word, environment: Environment) => ShellWord | null;
24
+ export { DYNAMIC_VALUE, BACKGROUND_PID_VALUE, backgroundPidWord, boundWord, cloneEnvironment, emptyEnvironment, isQuotedPart, isTrustedMktemp, staticGeneratedValue, trustedTempWord, wordIsStatic, };
25
+ export type { Environment };
@@ -0,0 +1,21 @@
1
+ import type { ShellInvocation, ShellWord } from './types';
2
+ declare const SHELL_WRAPPER_HEADS: ReadonlySet<string>;
3
+ declare const HEAD_RENAMING_HEADS: ReadonlySet<string>;
4
+ declare const skipOptions: (words: readonly ShellWord[], start: number, valueFlags: ReadonlySet<string>) => number;
5
+ declare const skipHeadRenamingPrefix: (invocation: ShellInvocation) => number;
6
+ declare const commandFlagValueIndex: (words: readonly ShellWord[], flag: string) => number | null;
7
+ declare const namedFlagValueIndex: (words: readonly ShellWord[], flags: ReadonlySet<string>, start?: number) => number | null;
8
+ declare const pickExecRoot: (words: readonly ShellWord[], execFlagIndex: number, kind: 'fd' | 'find') => ShellWord;
9
+ declare const FD_EXEC_FLAGS: ReadonlySet<string>;
10
+ declare const FD_PLACEHOLDERS: ReadonlySet<string>;
11
+ declare const FIND_EXEC_FLAGS: ReadonlySet<string>;
12
+ declare const FIND_PLACEHOLDERS: ReadonlySet<string>;
13
+ declare const RTK_WRAPPER_SUBCOMMANDS: ReadonlySet<string>;
14
+ interface PrefixWrapper {
15
+ readonly valueFlags: ReadonlySet<string>;
16
+ readonly skipPositionals: number;
17
+ readonly shellFlag: boolean;
18
+ }
19
+ declare const PREFIX_WRAPPERS: Readonly<Record<string, PrefixWrapper>>;
20
+ export type { PrefixWrapper };
21
+ export { FD_EXEC_FLAGS, FD_PLACEHOLDERS, FIND_EXEC_FLAGS, FIND_PLACEHOLDERS, HEAD_RENAMING_HEADS, PREFIX_WRAPPERS, RTK_WRAPPER_SUBCOMMANDS, SHELL_WRAPPER_HEADS, commandFlagValueIndex, namedFlagValueIndex, pickExecRoot, skipHeadRenamingPrefix, skipOptions, };
@@ -1,4 +1,6 @@
1
1
  import { Effect, Schema } from 'effect';
2
+ import { type ExecutionCarrierAlias } from './bash/config';
3
+ import { type RuleActions } from './rule-actions';
2
4
  declare const BlockRuleSchema: Schema.Struct<{
3
5
  readonly pattern: Schema.String;
4
6
  readonly message: Schema.String;
@@ -72,10 +74,35 @@ declare const ConfigSchema: Schema.Struct<{
72
74
  readonly values: Schema.$Array<Schema.String>;
73
75
  }>>>;
74
76
  }>>>;
77
+ readonly ruleActions: Schema.optional<Schema.Struct<{
78
+ readonly 'brew-mutation': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
79
+ readonly 'chmod-777-recursive': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
80
+ readonly 'defaults-write': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
81
+ readonly 'dscl-mutate': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
82
+ readonly 'launchctl-mutation': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
83
+ readonly 'mas-mutation': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
84
+ readonly 'no-gpg-sign': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
85
+ readonly 'no-verify': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
86
+ readonly osascript: Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
87
+ readonly 'pmset-write': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
88
+ readonly 'rsync-delete': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
89
+ readonly 'scutil-set': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
90
+ readonly 'security-keychain-add-write': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
91
+ readonly 'softwareupdate-install': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
92
+ readonly sudo: Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
93
+ readonly systemsetup: Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
94
+ readonly topgrade: Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
95
+ }>>;
75
96
  readonly secretScanner: Schema.optional<Schema.Struct<{
76
97
  readonly executable: Schema.String;
77
98
  readonly timeoutMs: Schema.Finite;
78
99
  }>>;
100
+ readonly shell: Schema.optional<Schema.Struct<{
101
+ readonly executionCarrierAliases: Schema.optional<Schema.$Array<Schema.Struct<{
102
+ readonly command: Schema.$Array<Schema.NonEmptyString>;
103
+ readonly equivalentTo: Schema.Literal<"ssh">;
104
+ }>>>;
105
+ }>>;
79
106
  }>;
80
107
  declare const CONFIG_PATH: string;
81
108
  declare const getDefaultConfig: () => ResolvedConfig;
@@ -94,6 +121,7 @@ export type GitConfig = typeof GitConfigSchema.Type;
94
121
  export type SafePathsConfig = typeof SafePathsConfigSchema.Type;
95
122
  export type ToolPolicy = typeof ToolPolicySchema.Type;
96
123
  export type SecretScannerConfig = typeof SecretScannerConfigSchema.Type;
124
+ export type { ExecutionCarrierAlias, ShellConfig } from './bash/config';
97
125
  export type Config = typeof ConfigSchema.Type;
98
126
  export interface ResolvedConfig {
99
127
  readonly git: {
@@ -104,7 +132,11 @@ export interface ResolvedConfig {
104
132
  readonly toolPolicies: readonly ToolPolicy[];
105
133
  readonly blockedCommands: readonly BlockRule[];
106
134
  readonly allowedCommands: readonly BlockRule[];
135
+ readonly ruleActions: RuleActions;
107
136
  readonly secretScanner: SecretScannerConfig;
137
+ readonly shell: {
138
+ readonly executionCarrierAliases: readonly ExecutionCarrierAlias[];
139
+ };
108
140
  }
109
141
  export type { ConfigLoad };
110
142
  export { CONFIG_PATH, ConfigSchema, SecretScannerConfigSchema, getDefaultConfig, mergeWithDefaults, };
@@ -0,0 +1,27 @@
1
+ import { Schema } from 'effect';
2
+ import { type Decision } from './decision';
3
+ declare const RuleActionSchema: Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>;
4
+ declare const RuleActionsSchema: Schema.Struct<{
5
+ readonly 'brew-mutation': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
6
+ readonly 'chmod-777-recursive': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
7
+ readonly 'defaults-write': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
8
+ readonly 'dscl-mutate': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
9
+ readonly 'launchctl-mutation': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
10
+ readonly 'mas-mutation': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
11
+ readonly 'no-gpg-sign': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
12
+ readonly 'no-verify': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
13
+ readonly osascript: Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
14
+ readonly 'pmset-write': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
15
+ readonly 'rsync-delete': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
16
+ readonly 'scutil-set': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
17
+ readonly 'security-keychain-add-write': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
18
+ readonly 'softwareupdate-install': Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
19
+ readonly sudo: Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
20
+ readonly systemsetup: Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
21
+ readonly topgrade: Schema.optional<Schema.Literals<readonly ["allow", "warn", "ask", "deny"]>>;
22
+ }>;
23
+ type RuleAction = typeof RuleActionSchema.Type;
24
+ type RuleActions = typeof RuleActionsSchema.Type;
25
+ declare const applyRuleAction: (decision: Decision, actions: RuleActions) => Decision;
26
+ export type { RuleAction, RuleActions };
27
+ export { applyRuleAction, RuleActionSchema, RuleActionsSchema };
@@ -0,0 +1,5 @@
1
+ import type { ShellProgram } from '../lib/bash';
2
+ import { type Decision } from '../lib/decision';
3
+ declare const NON_BYPASSABLE_RULES: ReadonlySet<string>;
4
+ declare const applyShellBypass: (program: ShellProgram, decision: Decision) => Decision;
5
+ export { applyShellBypass, NON_BYPASSABLE_RULES };
@@ -1,5 +1,5 @@
1
- import { type Segment } from '../lib/bash';
1
+ import type { ShellProgram } from '../lib/bash';
2
2
  import { type Decision } from '../lib/decision';
3
- declare const UNBYPASSABLE_RULES: ReadonlySet<string>;
4
- declare const bashDeny: (segments: readonly Segment[], cmd: string) => Decision;
5
- export { bashDeny, UNBYPASSABLE_RULES };
3
+ import { type RuleActions } from '../lib/rule-actions';
4
+ declare const bashDeny: (program: ShellProgram, ruleActions?: RuleActions) => Decision;
5
+ export { bashDeny };
@@ -1,5 +1,5 @@
1
- import { type Segment } from '../lib/bash';
1
+ import type { ShellProgram } from '../lib/bash';
2
2
  import type { GitConfig } from '../lib/config';
3
3
  import { type Decision } from '../lib/decision';
4
- declare const bashGit: (segments: readonly Segment[], cmd: string, config: GitConfig) => Decision;
4
+ declare const bashGit: (program: ShellProgram, config: GitConfig) => Decision;
5
5
  export { bashGit };
@@ -1,4 +1,4 @@
1
- import { type Segment } from '../lib/bash';
1
+ import type { ShellProgram } from '../lib/bash';
2
2
  import { type Decision } from '../lib/decision';
3
- declare const bashNetworkInstall: (segments: readonly Segment[], cmd: string) => Decision;
3
+ declare const bashNetworkInstall: (program: ShellProgram) => Decision;
4
4
  export { bashNetworkInstall };
@@ -1,4 +1,4 @@
1
- import { type Segment } from '../lib/bash';
1
+ import type { ShellProgram } from '../lib/bash';
2
2
  import { type Decision } from '../lib/decision';
3
- declare const bashRedirect: (segments: readonly Segment[], cmd: string) => Decision;
3
+ declare const bashRedirect: (program: ShellProgram) => Decision;
4
4
  export { bashRedirect };
@@ -1,5 +1,5 @@
1
- import { type Segment } from '../lib/bash';
1
+ import { type ShellProgram } from '../lib/bash';
2
2
  import type { SafePathsConfig } from '../lib/config';
3
3
  import { type Decision } from '../lib/decision';
4
- declare const bashScopedRm: (segments: readonly Segment[], cmd: string, config: SafePathsConfig) => Decision;
4
+ declare const bashScopedRm: (program: ShellProgram, config: SafePathsConfig) => Decision;
5
5
  export { bashScopedRm };
@@ -1,4 +1,4 @@
1
- import { type Segment } from '../lib/bash';
1
+ import type { ShellProgram } from '../lib/bash';
2
2
  import { type Decision } from '../lib/decision';
3
- declare const bashTarExplosion: (segments: readonly Segment[], cmd: string) => Decision;
3
+ declare const bashTarExplosion: (program: ShellProgram) => Decision;
4
4
  export { bashTarExplosion };
@@ -1,6 +1,6 @@
1
- import { type Segment } from '../lib/bash';
1
+ import { type ShellProgram } from '../lib/bash';
2
2
  import type { BlockRule } from '../lib/config';
3
3
  import { type Decision } from '../lib/decision';
4
- declare const matchPattern: (segments: readonly Segment[], rule: BlockRule) => boolean;
5
- export declare const configCustom: (segments: readonly Segment[], cmd: string, blockedCommands: readonly BlockRule[], allowedCommands: readonly BlockRule[]) => Decision;
4
+ declare const matchPattern: (program: ShellProgram, rule: BlockRule) => boolean;
5
+ export declare const configCustom: (program: ShellProgram, blockedCommands: readonly BlockRule[], allowedCommands: readonly BlockRule[]) => Decision;
6
6
  export { matchPattern };
@@ -1,5 +1,5 @@
1
- import { type Segment } from '../lib/bash';
1
+ import type { ShellProgram } from '../lib/bash';
2
2
  import type { ToolPolicy } from '../lib/config';
3
3
  import { type Decision } from '../lib/decision';
4
- declare const toolPolicy: (segments: readonly Segment[], command: string, policies: readonly ToolPolicy[]) => Decision;
4
+ declare const toolPolicy: (program: ShellProgram, policies: readonly ToolPolicy[]) => Decision;
5
5
  export { toolPolicy };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seanmozeik/tripwire",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "Configurable safety hooks for Claude Code, Codex, Cursor, Pi, and Oh My Pi",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -60,12 +60,12 @@
60
60
  "verify": "bun run format:check && bun run lint && bun run typecheck && bun test && bun run build"
61
61
  },
62
62
  "dependencies": {
63
- "shell-quote": "1.10.0"
63
+ "unbash": "4.0.10"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@effect/platform-bun": "4.0.0-rc.112",
67
67
  "@effect/tsgo": "0.38.0",
68
- "@seanmozeik/de-clank": "0.1.7",
68
+ "@seanmozeik/de-clank": "0.1.8",
69
69
  "bun-types": "1.4.0",
70
70
  "effect": "4.0.0-rc.112",
71
71
  "oxfmt": "0.65.0",
@@ -78,7 +78,7 @@
78
78
  "effect": "4.0.0-rc.112"
79
79
  },
80
80
  "optionalDependencies": {
81
- "@seanmozeik/tripwire-darwin-arm64": "0.7.2"
81
+ "@seanmozeik/tripwire-darwin-arm64": "0.8.0"
82
82
  },
83
83
  "engines": {
84
84
  "bun": ">=1.4"
@@ -1,27 +0,0 @@
1
- interface Segment {
2
- readonly head: string;
3
- readonly tokens: readonly string[];
4
- readonly args: readonly string[];
5
- readonly flags: readonly string[];
6
- readonly redirects: readonly Redirect[];
7
- readonly raw: string;
8
- }
9
- interface Redirect {
10
- readonly op: '>' | '>>' | '<' | '<<' | '<<<' | '<>' | '>&' | '<&' | '&>' | '&>>';
11
- readonly target: string;
12
- }
13
- declare const collectHeredocBodies: (cmd: string) => ReadonlyMap<string, string>;
14
- interface ExecSpec {
15
- readonly execFlags: ReadonlySet<string>;
16
- readonly placeholders: ReadonlySet<string>;
17
- readonly pickRoot: (tokens: readonly string[], execFlagIdx: number) => string;
18
- }
19
- declare const EXEC_SPECS: Readonly<Record<string, ExecSpec>>;
20
- declare const unwrapStaticString: (value: string, heredocBodies?: ReadonlyMap<string, string>) => string;
21
- declare const UNSUPPORTED_SHELL_HEAD = "__tripwire_unsupported_shell__";
22
- declare const parseCommand: (cmd: string) => Segment[];
23
- declare const isSafePathTarget: (raw: string, extraRelative?: readonly string[], extraAbsolute?: readonly string[]) => boolean;
24
- declare const safeScopesSummary: (extraRelative?: readonly string[], extraAbsolute?: readonly string[]) => string;
25
- declare const hasBypass: (cmd: string) => boolean;
26
- export type { Redirect, Segment };
27
- export { EXEC_SPECS, UNSUPPORTED_SHELL_HEAD, collectHeredocBodies, hasBypass, isSafePathTarget, parseCommand, safeScopesSummary, unwrapStaticString, };