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/README.md +64 -322
- package/assets/permissions.json +47 -0
- package/assets/varlock.config.json +7 -4
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +212 -0
- package/dist/config.js.map +1 -1
- package/dist/guard.d.ts.map +1 -1
- package/dist/guard.js +169 -1
- package/dist/guard.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +45 -2
- package/dist/plugin.js.map +1 -1
- package/dist/scrubber.d.ts +14 -0
- package/dist/scrubber.d.ts.map +1 -0
- package/dist/scrubber.js +33 -0
- package/dist/scrubber.js.map +1 -0
- package/dist/tools.d.ts +3 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +49 -16
- package/dist/tools.js.map +1 -1
- package/package.json +5 -1
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);
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAmC1C,MAAM,CAAC,MAAM,cAAc,GAAiB;IAC1C,KAAK,EAAE;QACL,OAAO,EAAE,IAAI;QACb,iBAAiB,EAAE;YACjB,MAAM;YACN,SAAS;YACT,MAAM;YACN,MAAM;YACN,aAAa;YACb,SAAS;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/guard.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
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":"AAAA;;;GAGG;AAMH,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;
|
|
1
|
+
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -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
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"}
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
}
|