@coo-quack/sensitive-canary 0.5.3 → 0.7.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.
@@ -10,7 +10,7 @@ import {
10
10
  parseAllowTags,
11
11
  randomBird,
12
12
  } from "./lib/inspector.ts";
13
- import { type Finding, scan } from "./lib/rules.ts";
13
+ import { enabledCategoriesFromEnv, type Finding, scan } from "./lib/rules.ts";
14
14
 
15
15
  interface HookInput {
16
16
  transcript_path?: string;
@@ -30,6 +30,8 @@ interface TranscriptLine {
30
30
  // Maximum bytes to read from the tail of a transcript file.
31
31
  const MAX_TRANSCRIPT_TAIL_BYTES = 65_536; // 64 KB
32
32
 
33
+ const ENABLED_CATEGORIES = enabledCategoriesFromEnv();
34
+
33
35
  // ── Transcript ────────────────────────────────────────────────────────────────
34
36
 
35
37
  // Returns true when the message contains at least one text content block
@@ -150,7 +152,8 @@ function extractFilePathsFromCommand(command: string): string[] {
150
152
 
151
153
  // ── .env pattern ──────────────────────────────────────────────────────────────
152
154
 
153
- // .env and .env.* (e.g. .env.local, .env.production) are blocked unconditionally.
155
+ // .env and .env.* (e.g. .env.local, .env.production) match the env filename pattern.
156
+ // The block only applies while the "secret" category is enabled (see shouldBlockEnvFile).
154
157
  // Files that merely end in .env (e.g. production.env) are handled by content scanning.
155
158
  function isBlockedEnvFile(filePath: string): boolean {
156
159
  if (!filePath) return false;
@@ -158,6 +161,12 @@ function isBlockedEnvFile(filePath: string): boolean {
158
161
  return base === ".env" || base.startsWith(".env.");
159
162
  }
160
163
 
164
+ // The .env name-based block is a secret guard: it only applies while the
165
+ // "secret" category is enabled.
166
+ function shouldBlockEnvFile(filePath: string): boolean {
167
+ return ENABLED_CATEGORIES.has("secret") && isBlockedEnvFile(filePath);
168
+ }
169
+
161
170
  // ── Output helpers ────────────────────────────────────────────────────────────
162
171
 
163
172
  // Build the allow-tag hint lines shown to Claude.
@@ -239,7 +248,7 @@ function block(
239
248
  // ── Core scan logic ───────────────────────────────────────────────────────────
240
249
 
241
250
  function scanFile(filePath: string, allowTags: Set<string>): void {
242
- if (isBlockedEnvFile(filePath)) {
251
+ if (shouldBlockEnvFile(filePath)) {
243
252
  if (allowTags.size > 0) return;
244
253
  block(
245
254
  filePath,
@@ -263,7 +272,10 @@ function scanFile(filePath: string, allowTags: Set<string>): void {
263
272
  return;
264
273
  }
265
274
 
266
- const findings = applyAllowTags(dedupeFindings(scan(content)), allowTags);
275
+ const findings = applyAllowTags(
276
+ dedupeFindings(scan(content, ENABLED_CATEGORIES)),
277
+ allowTags,
278
+ );
267
279
  if (findings.length === 0) return;
268
280
 
269
281
  block(
@@ -308,7 +320,10 @@ process.stdin.on("end", () => {
308
320
  for (const varName of extractEnvVarNames(command)) {
309
321
  const value = process.env[varName];
310
322
  if (!value) continue;
311
- const findings = applyAllowTags(dedupeFindings(scan(value)), allowTags);
323
+ const findings = applyAllowTags(
324
+ dedupeFindings(scan(value, ENABLED_CATEGORIES)),
325
+ allowTags,
326
+ );
312
327
  if (findings.length === 0) continue;
313
328
  block(
314
329
  `bash command: ${command.slice(0, 80)}`,
@@ -322,7 +337,7 @@ process.stdin.on("end", () => {
322
337
  }
323
338
 
324
339
  const cmdFindings = applyAllowTags(
325
- dedupeFindings(scan(command)),
340
+ dedupeFindings(scan(command, ENABLED_CATEGORIES)),
326
341
  allowTags,
327
342
  );
328
343
  if (cmdFindings.length > 0) {
@@ -8,12 +8,14 @@ import {
8
8
  randomBird,
9
9
  resolveTagPriority,
10
10
  } from "./lib/inspector.ts";
11
- import { scan } from "./lib/rules.ts";
11
+ import { enabledCategoriesFromEnv, scan } from "./lib/rules.ts";
12
12
 
13
13
  interface HookInput {
14
14
  prompt?: string;
15
15
  }
16
16
 
17
+ const ENABLED_CATEGORIES = enabledCategoriesFromEnv();
18
+
17
19
  let raw = "";
18
20
  process.stdin.setEncoding("utf8");
19
21
  process.stdin.on("data", (chunk: string) => (raw += chunk));
@@ -27,7 +29,7 @@ process.stdin.on("end", () => {
27
29
 
28
30
  const prompt = data.prompt ?? "";
29
31
 
30
- const allFindings = scan(prompt);
32
+ const allFindings = scan(prompt, ENABLED_CATEGORIES);
31
33
 
32
34
  if (allFindings.length === 0) process.exit(0);
33
35