@coo-quack/sensitive-canary 0.4.2

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.
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ applyAllowTags,
5
+ dedupeFindings,
6
+ type Finding,
7
+ findingsToLines,
8
+ randomBird,
9
+ resolveTagPriority,
10
+ } from "./lib/inspector.ts";
11
+ import { scan } from "./lib/rules.ts";
12
+
13
+ interface HookInput {
14
+ prompt?: string;
15
+ }
16
+
17
+ let raw = "";
18
+ process.stdin.setEncoding("utf8");
19
+ process.stdin.on("data", (chunk: string) => (raw += chunk));
20
+ process.stdin.on("end", () => {
21
+ let data: HookInput;
22
+ try {
23
+ data = JSON.parse(raw) as HookInput;
24
+ } catch {
25
+ process.exit(0);
26
+ }
27
+
28
+ const prompt = data.prompt ?? "";
29
+
30
+ const allFindings = scan(prompt);
31
+
32
+ if (allFindings.length === 0) process.exit(0);
33
+
34
+ const { effectiveAllow, effectiveMask } = resolveTagPriority(prompt);
35
+
36
+ const afterAllow: Finding[] = dedupeFindings(
37
+ applyAllowTags(allFindings, effectiveAllow),
38
+ );
39
+
40
+ if (afterAllow.length === 0) process.exit(0);
41
+
42
+ const maskableFindings = afterAllow.filter(
43
+ (f) =>
44
+ (f.category === "secret" && effectiveMask.has("secret")) ||
45
+ (f.category === "pii" && effectiveMask.has("pii")),
46
+ );
47
+
48
+ if (maskableFindings.length > 0) {
49
+ const hasSecret = maskableFindings.some((f) => f.category === "secret");
50
+ const hasPii = maskableFindings.some((f) => f.category === "pii");
51
+ const usedTags = effectiveMask.has("all")
52
+ ? "[mask-all]"
53
+ : (["secret", "pii"] as const)
54
+ .filter((d) => effectiveMask.has(d))
55
+ .map((d) => `[mask-${d}]`)
56
+ .join(", ");
57
+ const maskBlockLines = [
58
+ "",
59
+ `${randomBird()} sensitive-canary: prompt masking is not supported`,
60
+ "",
61
+ ` ${usedTags} cannot mask prompt content.`,
62
+ " The following sensitive data was detected:",
63
+ "",
64
+ ...findingsToLines(dedupeFindings(maskableFindings)),
65
+ "",
66
+ " Please choose one of the following:",
67
+ "",
68
+ " 1. Manually redact the values above and resubmit",
69
+ " 2. To send as-is, add an allow tag to your prompt:",
70
+ ...(hasSecret ? [" [allow-secret] — allow secrets"] : []),
71
+ ...(hasPii ? [" [allow-pii] — allow PII"] : []),
72
+ " [allow-all] — bypass all sensitive-canary checks",
73
+ "",
74
+ ];
75
+ process.stderr.write(maskBlockLines.join("\n"));
76
+ process.exit(2);
77
+ }
78
+
79
+ const unique: Finding[] = afterAllow.filter(
80
+ (f) =>
81
+ !(f.category === "secret" && effectiveMask.has("secret")) &&
82
+ !(f.category === "pii" && effectiveMask.has("pii")),
83
+ );
84
+
85
+ if (unique.length === 0) process.exit(0);
86
+
87
+ const hasSecret = unique.some((f) => f.category === "secret");
88
+ const hasPii = unique.some((f) => f.category === "pii");
89
+
90
+ const blockLines = [
91
+ "",
92
+ `${randomBird()} sensitive-canary: sensitive data detected — blocked`,
93
+ "",
94
+ ...findingsToLines(unique),
95
+ "",
96
+ "To allow, add a tag to your prompt:",
97
+ ...(hasSecret ? [" [allow-secret] — allow secrets"] : []),
98
+ ...(hasPii ? [" [allow-pii] — allow PII"] : []),
99
+ " [allow-all] — bypass all sensitive-canary checks",
100
+ "",
101
+ ];
102
+
103
+ process.stderr.write(blockLines.join("\n"));
104
+
105
+ process.exit(2);
106
+ });