opencode-varlock 0.0.8 → 0.0.10

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/config.js CHANGED
@@ -19,6 +19,7 @@ export const DEFAULT_CONFIG = {
19
19
  ".key",
20
20
  "credentials",
21
21
  ".pgpass",
22
+ "varlock.config",
22
23
  ],
23
24
  sensitiveGlobs: [
24
25
  "**/.env",
@@ -31,6 +32,8 @@ export const DEFAULT_CONFIG = {
31
32
  "**/credentials.*",
32
33
  "**/.pgpass",
33
34
  "secrets/**",
35
+ "**/varlock.config.json",
36
+ "**/.opencode/varlock.config.json",
34
37
  ],
35
38
  bashDenyPatterns: [],
36
39
  blockedReadTools: ["read", "grep", "glob", "view"],
@@ -47,6 +50,177 @@ export const DEFAULT_CONFIG = {
47
50
  namespace: "app",
48
51
  },
49
52
  };
53
+ /**
54
+ * Validates a parsed config object against the expected schema.
55
+ * Returns an array of human-readable error strings (empty = valid).
56
+ */
57
+ export function validateConfig(config, logger) {
58
+ const errors = [];
59
+ if (typeof config !== "object" || config === null) {
60
+ errors.push("Config must be an object");
61
+ return errors;
62
+ }
63
+ const c = config;
64
+ // Validate guard section
65
+ if (c.guard !== undefined) {
66
+ if (typeof c.guard !== "object" || c.guard === null) {
67
+ errors.push("guard must be an object");
68
+ }
69
+ else {
70
+ if (c.guard.enabled !== undefined &&
71
+ typeof c.guard.enabled !== "boolean") {
72
+ errors.push(`guard.enabled must be boolean, got ${typeof c.guard.enabled}`);
73
+ }
74
+ if (c.guard.sensitivePatterns !== undefined &&
75
+ !Array.isArray(c.guard.sensitivePatterns)) {
76
+ errors.push("guard.sensitivePatterns must be an array");
77
+ }
78
+ if (c.guard.sensitiveGlobs !== undefined &&
79
+ !Array.isArray(c.guard.sensitiveGlobs)) {
80
+ errors.push("guard.sensitiveGlobs must be an array");
81
+ }
82
+ if (c.guard.bashDenyPatterns !== undefined &&
83
+ !Array.isArray(c.guard.bashDenyPatterns)) {
84
+ errors.push("guard.bashDenyPatterns must be an array");
85
+ }
86
+ if (c.guard.blockedReadTools !== undefined &&
87
+ !Array.isArray(c.guard.blockedReadTools)) {
88
+ errors.push("guard.blockedReadTools must be an array");
89
+ }
90
+ if (c.guard.blockedWriteTools !== undefined &&
91
+ !Array.isArray(c.guard.blockedWriteTools)) {
92
+ errors.push("guard.blockedWriteTools must be an array");
93
+ }
94
+ // Validate array contents are strings
95
+ for (const arrKey of [
96
+ "sensitivePatterns",
97
+ "sensitiveGlobs",
98
+ "bashDenyPatterns",
99
+ "blockedReadTools",
100
+ "blockedWriteTools",
101
+ ]) {
102
+ if (Array.isArray(c.guard[arrKey])) {
103
+ for (let i = 0; i < c.guard[arrKey].length; i++) {
104
+ if (typeof c.guard[arrKey][i] !== "string") {
105
+ errors.push(`guard.${arrKey}[${i}] must be a string`);
106
+ }
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+ // Validate env section
113
+ if (c.env !== undefined) {
114
+ if (typeof c.env !== "object" || c.env === null) {
115
+ errors.push("env must be an object");
116
+ }
117
+ else {
118
+ if (c.env.enabled !== undefined && typeof c.env.enabled !== "boolean") {
119
+ errors.push(`env.enabled must be boolean, got ${typeof c.env.enabled}`);
120
+ }
121
+ if (c.env.allowedRoot !== undefined &&
122
+ typeof c.env.allowedRoot !== "string") {
123
+ errors.push("env.allowedRoot must be a string");
124
+ }
125
+ }
126
+ }
127
+ // Validate varlock section
128
+ if (c.varlock !== undefined) {
129
+ if (typeof c.varlock !== "object" || c.varlock === null) {
130
+ errors.push("varlock must be an object");
131
+ }
132
+ else {
133
+ if (c.varlock.enabled !== undefined &&
134
+ typeof c.varlock.enabled !== "boolean") {
135
+ errors.push(`varlock.enabled must be boolean, got ${typeof c.varlock.enabled}`);
136
+ }
137
+ if (c.varlock.autoDetect !== undefined &&
138
+ typeof c.varlock.autoDetect !== "boolean") {
139
+ errors.push(`varlock.autoDetect must be boolean, got ${typeof c.varlock.autoDetect}`);
140
+ }
141
+ if (c.varlock.command !== undefined &&
142
+ typeof c.varlock.command !== "string") {
143
+ errors.push("varlock.command must be a string");
144
+ }
145
+ if (c.varlock.namespace !== undefined &&
146
+ typeof c.varlock.namespace !== "string") {
147
+ errors.push("varlock.namespace must be a string");
148
+ }
149
+ }
150
+ }
151
+ return errors;
152
+ }
153
+ /**
154
+ * Removes invalid keys from a parsed config so they won't be merged.
155
+ * Mutates the object in place and returns the list of keys that were removed.
156
+ */
157
+ function sanitizeConfig(config) {
158
+ const removed = [];
159
+ const sections = [
160
+ {
161
+ name: "guard",
162
+ booleans: ["enabled"],
163
+ strings: [],
164
+ arrays: [
165
+ "sensitivePatterns",
166
+ "sensitiveGlobs",
167
+ "bashDenyPatterns",
168
+ "blockedReadTools",
169
+ "blockedWriteTools",
170
+ ],
171
+ },
172
+ {
173
+ name: "env",
174
+ booleans: ["enabled"],
175
+ strings: ["allowedRoot"],
176
+ arrays: [],
177
+ },
178
+ {
179
+ name: "varlock",
180
+ booleans: ["enabled", "autoDetect"],
181
+ strings: ["command", "namespace"],
182
+ arrays: [],
183
+ },
184
+ ];
185
+ for (const section of sections) {
186
+ const s = config[section.name];
187
+ if (s === undefined)
188
+ continue;
189
+ if (typeof s !== "object" || s === null) {
190
+ delete config[section.name];
191
+ removed.push(section.name);
192
+ continue;
193
+ }
194
+ for (const key of section.booleans) {
195
+ if (s[key] !== undefined && typeof s[key] !== "boolean") {
196
+ delete s[key];
197
+ removed.push(`${section.name}.${key}`);
198
+ }
199
+ }
200
+ for (const key of section.strings) {
201
+ if (s[key] !== undefined && typeof s[key] !== "string") {
202
+ delete s[key];
203
+ removed.push(`${section.name}.${key}`);
204
+ }
205
+ }
206
+ for (const key of section.arrays) {
207
+ if (s[key] !== undefined && !Array.isArray(s[key])) {
208
+ delete s[key];
209
+ removed.push(`${section.name}.${key}`);
210
+ }
211
+ else if (Array.isArray(s[key])) {
212
+ // Filter out non-string elements from arrays
213
+ const original = s[key];
214
+ const filtered = original.filter((v) => typeof v === "string");
215
+ if (filtered.length !== original.length) {
216
+ s[key] = filtered;
217
+ removed.push(`${section.name}.${key} (non-string elements removed)`);
218
+ }
219
+ }
220
+ }
221
+ }
222
+ return removed;
223
+ }
50
224
  const CONFIG_FILENAMES = [
51
225
  "varlock.config.json",
52
226
  ".opencode/varlock.config.json",
@@ -61,6 +235,24 @@ export function loadConfig(cwd, overrides = {}, logger) {
61
235
  const parsed = JSON.parse(raw);
62
236
  delete parsed.$schema;
63
237
  delete parsed.$comment;
238
+ // Validate and warn about any schema violations
239
+ const validationErrors = validateConfig(parsed, logger);
240
+ if (validationErrors.length > 0) {
241
+ logger?.({
242
+ level: "warn",
243
+ message: "config validation warnings",
244
+ extra: { filepath, errors: validationErrors },
245
+ });
246
+ }
247
+ // Remove invalid values so they don't override safe defaults
248
+ const removedKeys = sanitizeConfig(parsed);
249
+ if (removedKeys.length > 0) {
250
+ logger?.({
251
+ level: "warn",
252
+ message: "invalid config values removed before merge",
253
+ extra: { filepath, removedKeys },
254
+ });
255
+ }
64
256
  merged = deepMerge(merged, parsed);
65
257
  logger?.({
66
258
  level: "info",
@@ -80,6 +272,26 @@ export function loadConfig(cwd, overrides = {}, logger) {
80
272
  }
81
273
  }
82
274
  }
275
+ // Validate and sanitize programmatic overrides the same way
276
+ if (overrides && typeof overrides === "object") {
277
+ const overridesObj = overrides;
278
+ const overrideErrors = validateConfig(overridesObj, logger);
279
+ if (overrideErrors.length > 0) {
280
+ logger?.({
281
+ level: "warn",
282
+ message: "config validation warnings in programmatic overrides",
283
+ extra: { errors: overrideErrors },
284
+ });
285
+ }
286
+ const removedKeys = sanitizeConfig(overridesObj);
287
+ if (removedKeys.length > 0) {
288
+ logger?.({
289
+ level: "warn",
290
+ message: "invalid override values removed before merge",
291
+ extra: { removedKeys },
292
+ });
293
+ }
294
+ }
83
295
  merged = deepMerge(merged, overrides);
84
296
  if (merged.env.allowedRoot && !isAbsolute(merged.env.allowedRoot)) {
85
297
  merged.env.allowedRoot = resolve(cwd, merged.env.allowedRoot);
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE9C,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AA0C9C,KAAK,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AACjC,KAAK,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,CAAA;AAe/C,wBAAgB,cAAc,CAC5B,MAAM,EAAE,WAAW,GAClB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAwFzD;AAsBD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAuChD"}
1
+ {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE9C,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AA0J9C,KAAK,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AACjC,KAAK,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,CAAA;AAG/C,wBAAgB,cAAc,CAC5B,MAAM,EAAE,WAAW,GAClB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAgMzD;AAyDD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAuChD"}
package/dist/guard.js CHANGED
@@ -2,6 +2,16 @@
2
2
  * EnvGuard - tool.execute.before hook that prevents agents from
3
3
  * reading sensitive files or running commands that expose secrets.
4
4
  */
5
+ /* ------------------------------------------------------------------ *
6
+ * Task 6: Safe env file patterns that should NOT be blocked
7
+ * ------------------------------------------------------------------ */
8
+ const SAFE_ENV_PATTERNS = [".env.schema", ".env.example", ".env.sample"];
9
+ /* ------------------------------------------------------------------ *
10
+ * Task 1: Block varlock CLI self-exfiltration
11
+ * Task 8: Block set/compgen/declare/typeset variable listing
12
+ * Task 10: Block targeted recursive grep for .env files
13
+ * (Existing deny patterns preserved)
14
+ * ------------------------------------------------------------------ */
5
15
  const BUILTIN_BASH_DENY = [
6
16
  "cat .env",
7
17
  "less .env",
@@ -40,7 +50,24 @@ const BUILTIN_BASH_DENY = [
40
50
  'find . -name "*.env"',
41
51
  "curl.*env",
42
52
  "wget.*env",
53
+ // Task 1: varlock CLI self-exfiltration
54
+ "varlock printenv",
55
+ "varlock load --show-all",
56
+ "varlock load --format env",
57
+ "varlock load --format shell",
58
+ "varlock load -f env",
59
+ "varlock load -f shell",
60
+ // Task 8: compgen/typeset variable listing
61
+ "compgen -v",
62
+ "compgen -A variable",
63
+ "typeset -x",
64
+ // Task 10: recursive grep targeting .env files
65
+ "grep --include=*.env*",
66
+ "rg -g '*.env*'",
43
67
  ];
68
+ /* ------------------------------------------------------------------ *
69
+ * Existing: Regex patterns for runtime env-value reads via interpreters
70
+ * ------------------------------------------------------------------ */
44
71
  const ENV_VALUE_READ_PATTERNS = [
45
72
  /\bpython\d*\b[\s\S]*\bos\.getenv\s*\(/i,
46
73
  /\bpython\d*\b[\s\S]*\bos\.environ(?:\s*\[|\s*\.get\s*\()/i,
@@ -52,6 +79,44 @@ const ENV_VALUE_READ_PATTERNS = [
52
79
  /\bjava\b[\s\S]*\bSystem\.getenv\s*\(/i,
53
80
  /\bperl\b[\s\S]*\bENV\s*\{/i,
54
81
  ];
82
+ /* ------------------------------------------------------------------ *
83
+ * Task 2: Sensitive file read via interpreter (open(), File.read, etc.)
84
+ * Catches python open('.env'), ruby File.read('.env'), node fs.readFileSync('.env'), etc.
85
+ * ------------------------------------------------------------------ */
86
+ const SENSITIVE_FILE_SUFFIX_RE = "(?:\\.env\\b|\\.pem\\b|\\.key\\b|credentials|\\.pgpass|\\.secret)";
87
+ const SENSITIVE_FILE_READ_VIA_INTERPRETER = [
88
+ // Python: open(), pathlib, Path()
89
+ new RegExp(`\\bpython\\d*\\b[\\s\\S]*\\bopen\\s*\\([\\s\\S]*${SENSITIVE_FILE_SUFFIX_RE}`, "i"),
90
+ new RegExp(`\\bpython\\d*\\b[\\s\\S]*pathlib[\\s\\S]*${SENSITIVE_FILE_SUFFIX_RE}`, "i"),
91
+ new RegExp(`\\bpython\\d*\\b[\\s\\S]*\\bPath\\s*\\([\\s\\S]*${SENSITIVE_FILE_SUFFIX_RE}`, "i"),
92
+ // Ruby: File.read, File.open, IO.read
93
+ new RegExp(`\\bruby\\b[\\s\\S]*(?:File\\.(?:read|open)|IO\\.read)\\s*\\([\\s\\S]*${SENSITIVE_FILE_SUFFIX_RE}`, "i"),
94
+ // Node: fs.readFileSync, readFileSync, readFile
95
+ new RegExp(`\\bnode\\b[\\s\\S]*(?:readFileSync|readFile)\\s*\\([\\s\\S]*${SENSITIVE_FILE_SUFFIX_RE}`, "i"),
96
+ // Bun: Bun.file, readFileSync
97
+ new RegExp(`\\bbun\\b[\\s\\S]*(?:Bun\\.file|readFileSync)\\s*\\([\\s\\S]*${SENSITIVE_FILE_SUFFIX_RE}`, "i"),
98
+ // Perl: open()
99
+ new RegExp(`\\bperl\\b[\\s\\S]*\\bopen\\s*\\([\\s\\S]*${SENSITIVE_FILE_SUFFIX_RE}`, "i"),
100
+ // PHP: file_get_contents, fopen, file
101
+ new RegExp(`\\bphp\\b[\\s\\S]*(?:file_get_contents|fopen|\\bfile\\b)\\s*\\([\\s\\S]*${SENSITIVE_FILE_SUFFIX_RE}`, "i"),
102
+ ];
103
+ /* ------------------------------------------------------------------ *
104
+ * Task 5: Encoding / eval bypass patterns — pipe to shell, eval subshell
105
+ * ------------------------------------------------------------------ */
106
+ const PIPE_TO_SHELL_RE = /\|\s*(?:bash|sh|zsh|dash)\s*$/im;
107
+ const EVAL_SUBSHELL_RE = /\beval\s+"\$\(/i;
108
+ /* ------------------------------------------------------------------ *
109
+ * Task 7: printf format string bypass — catches printf with $VAR reference
110
+ * ------------------------------------------------------------------ */
111
+ const PRINTF_VAR_RE = /\bprintf\b[\s\S]*\$[A-Za-z_]/i;
112
+ /* ------------------------------------------------------------------ *
113
+ * Task 8: bare `set` piped or at end of command (set | grep, etc.)
114
+ * ------------------------------------------------------------------ */
115
+ const SET_PIPE_RE = /\bset\s*(\||$|>)/im;
116
+ /* ------------------------------------------------------------------ *
117
+ * Task 9: bare `env` command detection (fixed from literal \n)
118
+ * ------------------------------------------------------------------ */
119
+ const BARE_ENV_RE = /(?:^|\s|;|&&|\|\|)env\s*$/im;
55
120
  export function createEnvGuard(config) {
56
121
  const { sensitivePatterns, sensitiveGlobs, bashDenyPatterns, blockedReadTools, blockedWriteTools, } = config;
57
122
  const bashDeny = [...BUILTIN_BASH_DENY, ...bashDenyPatterns];
@@ -59,6 +124,20 @@ export function createEnvGuard(config) {
59
124
  source: g,
60
125
  regex: globToRegex(g),
61
126
  }));
127
+ /* ---------------------------------------------------------------- *
128
+ * Task 4: Build shell redirect patterns dynamically from sensitivePatterns
129
+ * ---------------------------------------------------------------- */
130
+ const shellRedirectPatterns = [];
131
+ for (const sp of sensitivePatterns) {
132
+ // Skip safe patterns in redirect construction
133
+ if (SAFE_ENV_PATTERNS.some((safe) => sp.toLowerCase() === safe.toLowerCase())) {
134
+ continue;
135
+ }
136
+ const escaped = escapeRegex(sp);
137
+ shellRedirectPatterns.push(new RegExp(`\\b(?:read|mapfile|readarray)\\b[\\s\\S]*<[\\s\\S]*${escaped}`, "i"));
138
+ shellRedirectPatterns.push(new RegExp(`<\\s*\\S*${escaped}`, "i"));
139
+ shellRedirectPatterns.push(new RegExp(`\\bexec\\b[\\s\\S]*<[\\s\\S]*${escaped}`, "i"));
140
+ }
62
141
  return async (input, output) => {
63
142
  const args = output.args;
64
143
  if (blockedReadTools.includes(input.tool)) {
@@ -78,28 +157,90 @@ export function createEnvGuard(config) {
78
157
  if (input.tool === "bash") {
79
158
  const rawCommand = String(args.command ?? "");
80
159
  const cmd = rawCommand.toLowerCase();
160
+ /* -- Existing: runtime env-value read patterns -- */
81
161
  for (const pattern of ENV_VALUE_READ_PATTERNS) {
82
162
  if (pattern.test(rawCommand)) {
83
163
  throw new Error(`[varlock] Blocked: bash command appears to read environment variable values at runtime. ` +
84
164
  `Use the load_env or load_secrets tool instead.`);
85
165
  }
86
166
  }
167
+ /* -- Task 2: sensitive file read via interpreter -- */
168
+ for (const pattern of SENSITIVE_FILE_READ_VIA_INTERPRETER) {
169
+ if (pattern.test(rawCommand)) {
170
+ throw new Error(`[varlock] Blocked: bash command appears to read a sensitive file via interpreter. ` +
171
+ `Use the load_env or load_secrets tool instead.`);
172
+ }
173
+ }
174
+ /* -- Task 5: pipe-to-shell and eval subshell bypass -- */
175
+ if (PIPE_TO_SHELL_RE.test(rawCommand)) {
176
+ throw new Error(`[varlock] Blocked: bash command pipes output into a shell interpreter. ` +
177
+ `This pattern is not allowed for security reasons.`);
178
+ }
179
+ if (EVAL_SUBSHELL_RE.test(rawCommand)) {
180
+ throw new Error(`[varlock] Blocked: bash command uses eval with command substitution. ` +
181
+ `This pattern is not allowed for security reasons.`);
182
+ }
183
+ /* -- Task 7: printf with variable reference -- */
184
+ if (PRINTF_VAR_RE.test(rawCommand)) {
185
+ throw new Error(`[varlock] Blocked: bash command uses printf with a variable reference. ` +
186
+ `Use the load_env or load_secrets tool instead.`);
187
+ }
188
+ /* -- Task 8: bare set piped/redirected -- */
189
+ if (SET_PIPE_RE.test(rawCommand)) {
190
+ throw new Error(`[varlock] Blocked: bash command uses "set" in a way that may expose variables. ` +
191
+ `Use the load_env or load_secrets tool instead.`);
192
+ }
193
+ /* -- Task 9: bare env command -- */
194
+ if (BARE_ENV_RE.test(rawCommand)) {
195
+ throw new Error(`[varlock] Blocked: bare "env" command may expose environment variables. ` +
196
+ `Use the load_env or load_secrets tool instead.`);
197
+ }
198
+ /* -- Existing + Task 6 safe-pattern check: string deny patterns -- */
87
199
  for (const pattern of bashDeny) {
88
200
  if (cmd.includes(pattern.toLowerCase())) {
201
+ // Task 6: If the command only references safe env files, allow it
202
+ if (isSafeEnvReference(rawCommand)) {
203
+ continue;
204
+ }
89
205
  throw new Error(`[varlock] Blocked: bash command matches deny pattern "${pattern}". ` +
90
206
  `Use the load_env or load_secrets tool to access secrets.`);
91
207
  }
92
208
  }
209
+ /* -- Existing + Task 3: file viewer/processor regex with sensitive patterns -- */
93
210
  for (const sp of sensitivePatterns) {
94
- const fileAccessRe = new RegExp(`(cat|less|more|head|tail|bat|vim?|nano|code|type|get-content|select-string)\\s+\\S*${escapeRegex(sp)}`, "i");
211
+ // Task 6: skip safe env patterns
212
+ if (SAFE_ENV_PATTERNS.some((safe) => sp.toLowerCase() === safe.toLowerCase())) {
213
+ continue;
214
+ }
215
+ // Task 3: expanded list of file processors
216
+ // Use [\s\S]* between command and pattern to match across flags/arguments
217
+ const fileAccessRe = new RegExp(`(cat|less|more|head|tail|bat|vim?|nano|code|type|get-content|select-string|sed|awk|cut|sort|tac|rev|paste|dd|tee|xargs|nl|fold|strings|xxd|od|hexdump|column)\\s[\\s\\S]*${escapeRegex(sp)}`, "i");
95
218
  if (fileAccessRe.test(rawCommand)) {
219
+ // Task 6: allow safe env file references
220
+ if (isSafeEnvReference(rawCommand)) {
221
+ continue;
222
+ }
96
223
  throw new Error(`[varlock] Blocked: bash command appears to read a sensitive file (*${sp}*). ` +
97
224
  `Use the load_env or load_secrets tool instead.`);
98
225
  }
99
226
  }
227
+ /* -- Task 4: shell redirect patterns -- */
228
+ for (const pattern of shellRedirectPatterns) {
229
+ if (pattern.test(rawCommand)) {
230
+ if (isSafeEnvReference(rawCommand)) {
231
+ continue;
232
+ }
233
+ throw new Error(`[varlock] Blocked: bash command uses shell redirects to read a sensitive file. ` +
234
+ `Use the load_env or load_secrets tool instead.`);
235
+ }
236
+ }
100
237
  if (compiledGlobs.length > 0) {
101
238
  const tokens = extractPathTokens(rawCommand);
102
239
  for (const token of tokens) {
240
+ // Task 6: skip safe env file tokens
241
+ if (isSafeEnvToken(token)) {
242
+ continue;
243
+ }
103
244
  for (const { source, regex } of compiledGlobs) {
104
245
  if (regex.test(token)) {
105
246
  throw new Error(`[varlock] Blocked: bash command references "${token}" which matches glob "${source}". ` +
@@ -111,8 +252,35 @@ export function createEnvGuard(config) {
111
252
  }
112
253
  };
113
254
  }
255
+ /* ------------------------------------------------------------------ *
256
+ * Task 6: Safe env pattern helpers
257
+ * ------------------------------------------------------------------ */
258
+ /**
259
+ * Returns true if a path ends with a known safe env file suffix.
260
+ */
261
+ function isSafeEnvToken(token) {
262
+ const lower = token.toLowerCase();
263
+ return SAFE_ENV_PATTERNS.some((safe) => lower.endsWith(safe.toLowerCase()));
264
+ }
265
+ /**
266
+ * Returns true if the command ONLY references safe env files (e.g. .env.example)
267
+ * and does NOT reference actual secret env files.
268
+ * We check if every .env mention in the command is a safe one.
269
+ */
270
+ function isSafeEnvReference(command) {
271
+ // Find all .env references in the command
272
+ const envRefs = command.match(/\S*\.env\S*/gi);
273
+ if (!envRefs)
274
+ return false;
275
+ // Every .env reference must be a safe pattern
276
+ return envRefs.every((ref) => SAFE_ENV_PATTERNS.some((safe) => ref.toLowerCase().endsWith(safe.toLowerCase())));
277
+ }
114
278
  function isSensitive(path, patterns, globs) {
115
279
  const lower = path.toLowerCase();
280
+ // Task 6: allow safe env files through
281
+ if (SAFE_ENV_PATTERNS.some((safe) => lower.endsWith(safe.toLowerCase()))) {
282
+ return false;
283
+ }
116
284
  if (patterns.some((p) => lower.includes(p.toLowerCase()))) {
117
285
  return true;
118
286
  }
package/dist/guard.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"guard.js","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"guard.js","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH;;wEAEwE;AACxE,MAAM,iBAAiB,GAAG,CAAC,aAAa,EAAE,cAAc,EAAE,aAAa,CAAC,CAAA;AAExE;;;;;wEAKwE;AACxE,MAAM,iBAAiB,GAAG;IACxB,UAAU;IACV,WAAW;IACX,WAAW;IACX,WAAW;IACX,WAAW;IACX,UAAU;IACV,WAAW;IACX,UAAU;IACV,SAAS;IACT,WAAW;IACX,UAAU;IACV,QAAQ;IACR,SAAS;IACT,eAAe;IACf,OAAO;IACP,OAAO;IACP,WAAW;IACX,YAAY;IACZ,aAAa;IACb,YAAY;IACZ,YAAY;IACZ,SAAS;IACT,gBAAgB;IAChB,eAAe;IACf,QAAQ;IACR,aAAa;IACb,QAAQ;IACR,QAAQ;IACR,WAAW;IACX,SAAS;IACT,SAAS;IACT,UAAU;IACV,mBAAmB;IACnB,sBAAsB;IACtB,sBAAsB;IACtB,WAAW;IACX,WAAW;IAEX,wCAAwC;IACxC,kBAAkB;IAClB,yBAAyB;IACzB,2BAA2B;IAC3B,6BAA6B;IAC7B,qBAAqB;IACrB,uBAAuB;IAEvB,2CAA2C;IAC3C,YAAY;IACZ,qBAAqB;IACrB,YAAY;IAEZ,+CAA+C;IAC/C,uBAAuB;IACvB,gBAAgB;CACjB,CAAA;AAED;;wEAEwE;AACxE,MAAM,uBAAuB,GAAG;IAC9B,wCAAwC;IACxC,2DAA2D;IAC3D,kEAAkE;IAClE,iEAAiE;IACjE,uCAAuC;IACvC,6CAA6C;IAC7C,8BAA8B;IAC9B,uCAAuC;IACvC,4BAA4B;CAC7B,CAAA;AAED;;;wEAGwE;AACxE,MAAM,wBAAwB,GAC5B,mEAAmE,CAAA;AAErE,MAAM,mCAAmC,GAAa;IACpD,kCAAkC;IAClC,IAAI,MAAM,CACR,mDAAmD,wBAAwB,EAAE,EAC7E,GAAG,CACJ;IACD,IAAI,MAAM,CACR,4CAA4C,wBAAwB,EAAE,EACtE,GAAG,CACJ;IACD,IAAI,MAAM,CACR,mDAAmD,wBAAwB,EAAE,EAC7E,GAAG,CACJ;IACD,sCAAsC;IACtC,IAAI,MAAM,CACR,wEAAwE,wBAAwB,EAAE,EAClG,GAAG,CACJ;IACD,gDAAgD;IAChD,IAAI,MAAM,CACR,+DAA+D,wBAAwB,EAAE,EACzF,GAAG,CACJ;IACD,8BAA8B;IAC9B,IAAI,MAAM,CACR,gEAAgE,wBAAwB,EAAE,EAC1F,GAAG,CACJ;IACD,eAAe;IACf,IAAI,MAAM,CACR,6CAA6C,wBAAwB,EAAE,EACvE,GAAG,CACJ;IACD,sCAAsC;IACtC,IAAI,MAAM,CACR,2EAA2E,wBAAwB,EAAE,EACrG,GAAG,CACJ;CACF,CAAA;AAED;;wEAEwE;AACxE,MAAM,gBAAgB,GAAG,iCAAiC,CAAA;AAC1D,MAAM,gBAAgB,GAAG,iBAAiB,CAAA;AAE1C;;wEAEwE;AACxE,MAAM,aAAa,GAAG,+BAA+B,CAAA;AAErD;;wEAEwE;AACxE,MAAM,WAAW,GAAG,oBAAoB,CAAA;AAExC;;wEAEwE;AACxE,MAAM,WAAW,GAAG,6BAA6B,CAAA;AAMjD,MAAM,UAAU,cAAc,CAC5B,MAAmB;IAEnB,MAAM,EACJ,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,GAClB,GAAG,MAAM,CAAA;IAEV,MAAM,QAAQ,GAAG,CAAC,GAAG,iBAAiB,EAAE,GAAG,gBAAgB,CAAC,CAAA;IAC5D,MAAM,aAAa,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/C,MAAM,EAAE,CAAC;QACT,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC;KACtB,CAAC,CAAC,CAAA;IAEH;;0EAEsE;IACtE,MAAM,qBAAqB,GAAa,EAAE,CAAA;IAC1C,KAAK,MAAM,EAAE,IAAI,iBAAiB,EAAE,CAAC;QACnC,8CAA8C;QAC9C,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YAC9E,SAAQ;QACV,CAAC;QACD,MAAM,OAAO,GAAG,WAAW,CAAC,EAAE,CAAC,CAAA;QAC/B,qBAAqB,CAAC,IAAI,CACxB,IAAI,MAAM,CACR,sDAAsD,OAAO,EAAE,EAC/D,GAAG,CACJ,CACF,CAAA;QACD,qBAAqB,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,YAAY,OAAO,EAAE,EAAE,GAAG,CAAC,CAAC,CAAA;QAClE,qBAAqB,CAAC,IAAI,CACxB,IAAI,MAAM,CAAC,gCAAgC,OAAO,EAAE,EAAE,GAAG,CAAC,CAC3D,CAAA;IACH,CAAC;IAED,OAAO,KAAK,EAAE,KAAgB,EAAE,MAAkB,EAAE,EAAE;QACpD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAA;QAExB,IAAI,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,IAAI,EAAE,CAAA;YAC5E,IAAI,MAAM,IAAI,WAAW,CAAC,MAAM,EAAE,iBAAiB,EAAE,aAAa,CAAC,EAAE,CAAC;gBACpE,MAAM,IAAI,KAAK,CACb,4CAA4C,MAAM,KAAK;oBACrD,gDAAgD,CACnD,CAAA;YACH,CAAC;QACH,CAAC;QAED,IAAI,iBAAiB,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,EAAE,CAAA;YAC5D,IAAI,MAAM,IAAI,WAAW,CAAC,MAAM,EAAE,iBAAiB,EAAE,aAAa,CAAC,EAAE,CAAC;gBACpE,MAAM,IAAI,KAAK,CACb,uCAAuC,MAAM,KAAK;oBAChD,qDAAqD,CACxD,CAAA;YACH,CAAC;QACH,CAAC;QAED,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAA;YAC7C,MAAM,GAAG,GAAG,UAAU,CAAC,WAAW,EAAE,CAAA;YAEpC,qDAAqD;YACrD,KAAK,MAAM,OAAO,IAAI,uBAAuB,EAAE,CAAC;gBAC9C,IAAI,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;oBAC7B,MAAM,IAAI,KAAK,CACb,0FAA0F;wBACxF,gDAAgD,CACnD,CAAA;gBACH,CAAC;YACH,CAAC;YAED,uDAAuD;YACvD,KAAK,MAAM,OAAO,IAAI,mCAAmC,EAAE,CAAC;gBAC1D,IAAI,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;oBAC7B,MAAM,IAAI,KAAK,CACb,oFAAoF;wBAClF,gDAAgD,CACnD,CAAA;gBACH,CAAC;YACH,CAAC;YAED,0DAA0D;YAC1D,IAAI,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBACtC,MAAM,IAAI,KAAK,CACb,yEAAyE;oBACvE,mDAAmD,CACtD,CAAA;YACH,CAAC;YACD,IAAI,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBACtC,MAAM,IAAI,KAAK,CACb,uEAAuE;oBACrE,mDAAmD,CACtD,CAAA;YACH,CAAC;YAED,kDAAkD;YAClD,IAAI,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBACnC,MAAM,IAAI,KAAK,CACb,yEAAyE;oBACvE,gDAAgD,CACnD,CAAA;YACH,CAAC;YAED,6CAA6C;YAC7C,IAAI,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBACjC,MAAM,IAAI,KAAK,CACb,iFAAiF;oBAC/E,gDAAgD,CACnD,CAAA;YACH,CAAC;YAED,oCAAoC;YACpC,IAAI,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBACjC,MAAM,IAAI,KAAK,CACb,0EAA0E;oBACxE,gDAAgD,CACnD,CAAA;YACH,CAAC;YAED,sEAAsE;YACtE,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,IAAI,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;oBACxC,kEAAkE;oBAClE,IAAI,kBAAkB,CAAC,UAAU,CAAC,EAAE,CAAC;wBACnC,SAAQ;oBACV,CAAC;oBACD,MAAM,IAAI,KAAK,CACb,yDAAyD,OAAO,KAAK;wBACnE,0DAA0D,CAC7D,CAAA;gBACH,CAAC;YACH,CAAC;YAED,kFAAkF;YAClF,KAAK,MAAM,EAAE,IAAI,iBAAiB,EAAE,CAAC;gBACnC,iCAAiC;gBACjC,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;oBAC9E,SAAQ;gBACV,CAAC;gBACD,2CAA2C;gBAC3C,0EAA0E;gBAC1E,MAAM,YAAY,GAAG,IAAI,MAAM,CAC7B,4KAA4K,WAAW,CAAC,EAAE,CAAC,EAAE,EAC7L,GAAG,CACJ,CAAA;gBACD,IAAI,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;oBAClC,yCAAyC;oBACzC,IAAI,kBAAkB,CAAC,UAAU,CAAC,EAAE,CAAC;wBACnC,SAAQ;oBACV,CAAC;oBACD,MAAM,IAAI,KAAK,CACb,sEAAsE,EAAE,MAAM;wBAC5E,gDAAgD,CACnD,CAAA;gBACH,CAAC;YACH,CAAC;YAED,2CAA2C;YAC3C,KAAK,MAAM,OAAO,IAAI,qBAAqB,EAAE,CAAC;gBAC5C,IAAI,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;oBAC7B,IAAI,kBAAkB,CAAC,UAAU,CAAC,EAAE,CAAC;wBACnC,SAAQ;oBACV,CAAC;oBACD,MAAM,IAAI,KAAK,CACb,iFAAiF;wBAC/E,gDAAgD,CACnD,CAAA;gBACH,CAAC;YACH,CAAC;YAED,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,MAAM,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAA;gBAC5C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;oBAC3B,oCAAoC;oBACpC,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;wBAC1B,SAAQ;oBACV,CAAC;oBACD,KAAK,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,aAAa,EAAE,CAAC;wBAC9C,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;4BACtB,MAAM,IAAI,KAAK,CACb,+CAA+C,KAAK,yBAAyB,MAAM,KAAK;gCACtF,gDAAgD,CACnD,CAAA;wBACH,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC,CAAA;AACH,CAAC;AAED;;wEAEwE;AAExE;;GAEG;AACH,SAAS,cAAc,CAAC,KAAa;IACnC,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAA;IACjC,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;AAC7E,CAAC;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,OAAe;IACzC,0CAA0C;IAC1C,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAAA;IAC9C,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAA;IAE1B,8CAA8C;IAC9C,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAC3B,iBAAiB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAC9B,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAC/C,CACF,CAAA;AACH,CAAC;AAED,SAAS,WAAW,CAClB,IAAY,EACZ,QAAkB,EAClB,KAAqB;IAErB,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;IAEhC,uCAAuC;IACvC,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC;QACzE,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC;QAC1D,OAAO,IAAI,CAAA;IACb,CAAC;IAED,KAAK,MAAM,EAAE,KAAK,EAAE,IAAI,KAAK,EAAE,CAAC;QAC9B,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1C,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,MAAM,GAAG,EAAE,CAAA;IACf,IAAI,CAAC,GAAG,CAAC,CAAA;IAET,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;QAElB,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBACxB,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;oBACxB,MAAM,IAAI,UAAU,CAAA;oBACpB,CAAC,IAAI,CAAC,CAAA;gBACR,CAAC;qBAAM,CAAC;oBACN,MAAM,IAAI,IAAI,CAAA;oBACd,CAAC,IAAI,CAAC,CAAA;gBACR,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,OAAO,CAAA;gBACjB,CAAC,EAAE,CAAA;YACL,CAAC;QACH,CAAC;aAAM,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACtB,MAAM,IAAI,MAAM,CAAA;YAChB,CAAC,EAAE,CAAA;QACL,CAAC;aAAM,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAA;YACf,CAAC,EAAE,CAAA;QACL,CAAC;aAAM,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YACrC,MAAM,IAAI,SAAS,CAAA;YACnB,CAAC,EAAE,CAAA;QACL,CAAC;aAAM,IAAI,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;YACrC,MAAM,IAAI,IAAI,GAAG,EAAE,CAAA;YACnB,CAAC,EAAE,CAAA;QACL,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,EAAE,CAAA;YACZ,CAAC,EAAE,CAAA;QACL,CAAC;IACH,CAAC;IAED,OAAO,IAAI,MAAM,CAAC,IAAI,MAAM,GAAG,EAAE,GAAG,CAAC,CAAA;AACvC,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAW;IACpC,MAAM,OAAO,GAAG,4DAA4D,CAAA;IAC5E,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,IAAI,KAA6B,CAAA;IAEjC,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC5C,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QAC7B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACpB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,WAAW,CAAC,CAAS;IAC5B,OAAO,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAA;AACjD,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { default } from "./plugin.js";
2
+ export { createSecretRegistry, type SecretRegistry } from "./scrubber.js";
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AACrC,OAAO,EAAE,oBAAoB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAA"}
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export { default } from "./plugin.js";
2
+ export { createSecretRegistry } from "./scrubber.js";
2
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AACrC,OAAO,EAAE,oBAAoB,EAAuB,MAAM,eAAe,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAc,KAAK,YAAY,EAAE,KAAK,WAAW,EAAqB,MAAM,aAAa,CAAA;AAQhG,eAAO,MAAM,aAAa,EAAE,MAE3B,CAAA;AAED,wBAAgB,mBAAmB,CACjC,SAAS,GAAE,WAAW,CAAC,YAAY,CAAM,GACxC,MAAM,CA4ER;AAED,eAAe,aAAa,CAAA"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAc,KAAK,YAAY,EAAE,KAAK,WAAW,EAAqB,MAAM,aAAa,CAAA;AAShG,eAAO,MAAM,aAAa,EAAE,MAE3B,CAAA;AAED,wBAAgB,mBAAmB,CACjC,SAAS,GAAE,WAAW,CAAC,YAAY,CAAM,GACxC,MAAM,CAwHR;AAED,eAAe,aAAa,CAAA"}
package/dist/plugin.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { tool } from "@opencode-ai/plugin";
2
2
  import { loadConfig } from "./config.js";
3
3
  import { createEnvGuard } from "./guard.js";
4
+ import { createSecretRegistry } from "./scrubber.js";
4
5
  import { createLoadEnvTool, createLoadSecretsTool, createSecretStatusTool, } from "./tools.js";
5
6
  export const VarlockPlugin = async (ctx) => {
6
7
  return createVarlockPlugin()(ctx);
@@ -36,12 +37,13 @@ export function createVarlockPlugin(overrides = {}) {
36
37
  varlockAvailable = false;
37
38
  }
38
39
  }
40
+ const registry = createSecretRegistry();
39
41
  const tools = {};
40
42
  if (config.env.enabled) {
41
- tools.load_env = createLoadEnvTool(config.env);
43
+ tools.load_env = createLoadEnvTool(config.env, registry);
42
44
  }
43
45
  if (varlockAvailable) {
44
- tools.load_secrets = createLoadSecretsTool($, config.varlock);
46
+ tools.load_secrets = createLoadSecretsTool($, config.varlock, registry);
45
47
  tools.secret_status = createSecretStatusTool($, config.varlock);
46
48
  }
47
49
  const hookResult = {
@@ -70,6 +72,47 @@ export function createVarlockPlugin(overrides = {}) {
70
72
  if (config.guard.enabled) {
71
73
  hookResult["tool.execute.before"] = createEnvGuard(config.guard);
72
74
  }
75
+ hookResult["tool.execute.after"] = async (input, output) => {
76
+ // Scrub secret values from tool output
77
+ if (registry.size() > 0) {
78
+ if (typeof output.result === "string") {
79
+ output.result = registry.scrub(output.result);
80
+ }
81
+ if (typeof output.error === "string") {
82
+ output.error = registry.scrub(output.error);
83
+ }
84
+ }
85
+ // After file write/edit, run varlock scan to check for leaked secrets
86
+ if (varlockAvailable && (input.tool === "write" || input.tool === "edit")) {
87
+ const filePath = output.args?.filePath ?? output.args?.path ?? output.args?.file;
88
+ if (filePath) {
89
+ try {
90
+ const scanResult = await $ `${config.varlock.command} scan ${filePath}`.quiet();
91
+ if (scanResult.exitCode !== 0) {
92
+ await log({
93
+ level: "warn",
94
+ message: "varlock scan detected potential secret leak in written file",
95
+ extra: { file: filePath },
96
+ });
97
+ }
98
+ }
99
+ catch {
100
+ // scan not available or failed, non-critical
101
+ }
102
+ }
103
+ }
104
+ };
105
+ hookResult["shell.env"] = async (_input, _output) => {
106
+ // Log which secret-managed env vars are being passed to shell
107
+ // This is observability, not blocking — the guard handles blocking
108
+ if (registry.size() > 0) {
109
+ await log({
110
+ level: "debug",
111
+ message: "shell execution with managed secrets in environment",
112
+ extra: { managedVarCount: registry.size() },
113
+ });
114
+ }
115
+ };
73
116
  return hookResult;
74
117
  };
75
118
  }