@wrongstack/plugins 0.300.0 → 0.302.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -725,7 +725,9 @@ warning depending on `mode`.
725
725
 
726
726
  Protects sensitive paths from accidental writes/edits and destructive shell
727
727
  commands. Defaults cover lockfiles, `.env`, `.git`, and migration-like paths;
728
- use `mode: "warn"` for advisory-only enforcement.
728
+ use `mode: "warn"` for advisory-only enforcement. Writer globs are blocked when
729
+ their unresolved scope overlaps a protected path; narrow the target or add an
730
+ `allow` glob to exempt an intentional scope.
729
731
 
730
732
  ```jsonc
731
733
  {
@@ -1,5 +1,30 @@
1
1
  // src/config-validator/index.ts
2
2
  import { readFileSync, statSync } from "node:fs";
3
+
4
+ // src/runtime/index.ts
5
+ import { basename, extname, isAbsolute, relative, resolve } from "node:path";
6
+
7
+ // src/runtime/local-bin.ts
8
+ import { buildWin32CmdShimInvocation, resolveWin32Command } from "@wrongstack/tools/win32";
9
+
10
+ // src/runtime/index.ts
11
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
12
+ function hasLeadingDash(arg) {
13
+ return arg.length > 0 && arg.startsWith("-");
14
+ }
15
+ function withinProjectPath(projectRoot, candidate) {
16
+ if (candidate.length === 0 || candidate.length > 4096) return false;
17
+ if (hasLeadingDash(candidate)) return false;
18
+ const resolved = isAbsolute(candidate) ? resolve(candidate) : resolve(projectRoot, candidate);
19
+ const rel = relative(projectRoot, resolved);
20
+ return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
21
+ }
22
+ function withinProject(p) {
23
+ const cwd = process.cwd();
24
+ return withinProjectPath(cwd, p) || relative(cwd, p) === ".";
25
+ }
26
+
27
+ // src/config-validator/index.ts
3
28
  var state = {
4
29
  invocations: 0,
5
30
  filesChecked: 0,
@@ -79,6 +104,9 @@ function positionToLineCol(text, pos) {
79
104
  const col = pos - upTo.lastIndexOf("\n");
80
105
  return { line, col };
81
106
  }
107
+ function redactParseSnippet(message) {
108
+ return message.replace(/"[\s\S]{0,200}?"(?= is not valid JSON)/g, '"\u2026"');
109
+ }
82
110
  function validateJson(text, isJsonc, fileName) {
83
111
  const source = isJsonc ? stripJsonc(text) : text;
84
112
  try {
@@ -112,10 +140,10 @@ function validateJson(text, isJsonc, fileName) {
112
140
  const idx = source.indexOf(snippetMatch[1]);
113
141
  if (idx >= 0) {
114
142
  const { line } = positionToLineCol(source, idx);
115
- return [`JSON parse error near line ${line}: ${message.split("\n")[0]}`];
143
+ return [`JSON parse error near line ${line}`];
116
144
  }
117
145
  }
118
- return [`JSON parse error: ${message.split("\n")[0]}`];
146
+ return [`JSON parse error: ${redactParseSnippet(message.split("\n")[0] ?? "")}`];
119
147
  }
120
148
  }
121
149
  function validateYaml(text) {
@@ -252,10 +280,12 @@ var plugin = {
252
280
  const cfg = readConfig(api.config.extensions?.["config-validator"]);
253
281
  const hook = (input) => {
254
282
  if (!cfg.enabled) return;
283
+ if (input.toolResult?.isError) return;
255
284
  state.invocations += 1;
256
285
  const ti = input.toolInput ?? {};
257
286
  const raw = ti["path"] ?? ti["file_path"] ?? ti["filePath"];
258
287
  if (typeof raw !== "string" || raw.length === 0) return;
288
+ if (!withinProject(raw)) return;
259
289
  const lower = raw.toLowerCase();
260
290
  if (!cfg.extensions.some((ext) => lower.endsWith(ext))) return;
261
291
  let text;