@osovv/vv-opencode 0.2.5 → 0.3.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 +29 -13
- package/dist/cli.js +21 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/doctor.js +20 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/guardian.js +22 -0
- package/dist/commands/guardian.js.map +1 -1
- package/dist/commands/install.js +20 -0
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/status.js +20 -0
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/sync.js +20 -0
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/version.js +18 -0
- package/dist/commands/version.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/opencode.js +53 -0
- package/dist/lib/opencode.js.map +1 -1
- package/dist/lib/package.js +22 -0
- package/dist/lib/package.js.map +1 -1
- package/dist/lib/vvoc-paths.js +26 -0
- package/dist/lib/vvoc-paths.js.map +1 -1
- package/dist/plugins/guardian.js +42 -0
- package/dist/plugins/guardian.js.map +1 -1
- package/dist/plugins/memory-store.js +57 -0
- package/dist/plugins/memory-store.js.map +1 -1
- package/dist/plugins/memory.js +34 -0
- package/dist/plugins/memory.js.map +1 -1
- package/dist/plugins/secrets-redaction/config.d.ts +26 -0
- package/dist/plugins/secrets-redaction/config.js +158 -0
- package/dist/plugins/secrets-redaction/config.js.map +1 -0
- package/dist/plugins/secrets-redaction/deep.d.ts +4 -0
- package/dist/plugins/secrets-redaction/deep.js +78 -0
- package/dist/plugins/secrets-redaction/deep.js.map +1 -0
- package/dist/plugins/secrets-redaction/engine.d.ts +14 -0
- package/dist/plugins/secrets-redaction/engine.js +113 -0
- package/dist/plugins/secrets-redaction/engine.js.map +1 -0
- package/dist/plugins/secrets-redaction/index.d.ts +2 -0
- package/dist/plugins/secrets-redaction/index.js +124 -0
- package/dist/plugins/secrets-redaction/index.js.map +1 -0
- package/dist/plugins/secrets-redaction/patterns.d.ts +26 -0
- package/dist/plugins/secrets-redaction/patterns.js +85 -0
- package/dist/plugins/secrets-redaction/patterns.js.map +1 -0
- package/dist/plugins/secrets-redaction/restore.d.ts +2 -0
- package/dist/plugins/secrets-redaction/restore.js +25 -0
- package/dist/plugins/secrets-redaction/restore.js.map +1 -0
- package/dist/plugins/secrets-redaction/session.d.ts +30 -0
- package/dist/plugins/secrets-redaction/session.js +113 -0
- package/dist/plugins/secrets-redaction/session.js.map +1 -0
- package/dist/plugins/secrets-redaction.d.ts +1 -0
- package/dist/plugins/secrets-redaction.js +16 -0
- package/dist/plugins/secrets-redaction.js.map +1 -0
- package/package.json +5 -2
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// FILE: src/plugins/secrets-redaction/engine.ts
|
|
2
|
+
// VERSION: 1.0.0
|
|
3
|
+
// START_MODULE_CONTRACT
|
|
4
|
+
// PURPOSE: Core redaction engine — performs find/replace of secrets with placeholders in text.
|
|
5
|
+
// SCOPE: text scanning, match sorting, interval arithmetic, replacement
|
|
6
|
+
// DEPENDS: session, patterns
|
|
7
|
+
// LINKS: knowledge-graph://plugins/secrets-redaction
|
|
8
|
+
// ROLE: RUNTIME
|
|
9
|
+
// MAP_MODE: EXPORTS
|
|
10
|
+
// END_MODULE_CONTRACT
|
|
11
|
+
//
|
|
12
|
+
// START_MODULE_MAP
|
|
13
|
+
// redactText - replaces secrets in text with placeholders, returns changed text + match list
|
|
14
|
+
// END_MODULE_MAP
|
|
15
|
+
function subtractCovered(intervals) {
|
|
16
|
+
if (intervals.length === 0)
|
|
17
|
+
return [];
|
|
18
|
+
intervals.sort((a, b) => a[0] - b[0]);
|
|
19
|
+
const result = [];
|
|
20
|
+
let currentEnd = intervals[0][0];
|
|
21
|
+
for (const [start, end] of intervals) {
|
|
22
|
+
if (start > currentEnd) {
|
|
23
|
+
result.push([currentEnd, start]);
|
|
24
|
+
}
|
|
25
|
+
if (end > currentEnd) {
|
|
26
|
+
currentEnd = end;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
function insertCovered(intervals, newInterval) {
|
|
32
|
+
const merged = [...intervals, newInterval];
|
|
33
|
+
merged.sort((a, b) => a[0] - b[0]);
|
|
34
|
+
const result = [];
|
|
35
|
+
for (const interval of merged) {
|
|
36
|
+
if (result.length > 0 && interval[0] <= result[result.length - 1][1]) {
|
|
37
|
+
result[result.length - 1][1] = Math.max(result[result.length - 1][1], interval[1]);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
result.push([...interval]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
function sortByPositionDesc(rules, text) {
|
|
46
|
+
const allMatches = [];
|
|
47
|
+
for (const rule of rules) {
|
|
48
|
+
const re = new RegExp(rule.pattern.source, rule.pattern.flags.includes("g") ? rule.pattern.flags : `${rule.pattern.flags}g`);
|
|
49
|
+
let match;
|
|
50
|
+
while ((match = re.exec(text)) !== null) {
|
|
51
|
+
allMatches.push({ rule, match: match });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
allMatches.sort((a, b) => {
|
|
55
|
+
const aStart = a.match.index;
|
|
56
|
+
const bStart = b.match.index;
|
|
57
|
+
if (aStart !== bStart)
|
|
58
|
+
return bStart - aStart;
|
|
59
|
+
return (b.match[0].length - a.match[0].length);
|
|
60
|
+
});
|
|
61
|
+
return allMatches.map((x) => ({ rule: x.rule, matches: x.match }));
|
|
62
|
+
}
|
|
63
|
+
export function redactText(input, patternSet, session) {
|
|
64
|
+
if (!input || patternSet.rules.length === 0) {
|
|
65
|
+
return { text: input, matches: [] };
|
|
66
|
+
}
|
|
67
|
+
const sorted = sortByPositionDesc(patternSet.rules, input);
|
|
68
|
+
const covered = [];
|
|
69
|
+
const matches = [];
|
|
70
|
+
for (const { rule, matches: match } of sorted) {
|
|
71
|
+
const start = match.index;
|
|
72
|
+
const end = start + match[0].length;
|
|
73
|
+
const value = match[0];
|
|
74
|
+
if (patternSet.exclude.has(value.toLowerCase())) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const gaps = subtractCovered(covered);
|
|
78
|
+
const isInside = gaps.every(([gStart, gEnd]) => start >= gStart && end <= gEnd);
|
|
79
|
+
if (isInside)
|
|
80
|
+
continue;
|
|
81
|
+
const placeholder = session.getOrCreatePlaceholder(value, rule.category);
|
|
82
|
+
matches.push({ start, end, original: value, placeholder, category: rule.category });
|
|
83
|
+
const remainingGaps = [];
|
|
84
|
+
for (const [gStart, gEnd] of gaps) {
|
|
85
|
+
if (start >= gEnd || end <= gStart) {
|
|
86
|
+
remainingGaps.push([gStart, gEnd]);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
if (start > gStart) {
|
|
90
|
+
remainingGaps.push([gStart, start]);
|
|
91
|
+
}
|
|
92
|
+
if (end < gEnd) {
|
|
93
|
+
remainingGaps.push([end, gEnd]);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
covered.length = 0;
|
|
98
|
+
for (const g of remainingGaps) {
|
|
99
|
+
insertCovered(covered, g);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const sortedMatches = [...matches].sort((a, b) => a.start - b.start);
|
|
103
|
+
let result = "";
|
|
104
|
+
let lastIndex = 0;
|
|
105
|
+
for (const m of sortedMatches) {
|
|
106
|
+
result += input.slice(lastIndex, m.start);
|
|
107
|
+
result += m.placeholder;
|
|
108
|
+
lastIndex = m.end;
|
|
109
|
+
}
|
|
110
|
+
result += input.slice(lastIndex);
|
|
111
|
+
return { text: result, matches: sortedMatches };
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/plugins/secrets-redaction/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// FILE: src/plugins/secrets-redaction/index.ts
|
|
2
|
+
// VERSION: 1.0.0
|
|
3
|
+
// START_MODULE_CONTRACT
|
|
4
|
+
// PURPOSE: OpenCode plugin that redacts secrets from messages before LLM requests and restores them after.
|
|
5
|
+
// SCOPE: 3 hook handlers — chat.messages.transform, text.complete, tool.execute.before
|
|
6
|
+
// DEPENDS: session, engine, patterns, restore, deep, config
|
|
7
|
+
// LINKS: knowledge-graph://plugins/secrets-redaction
|
|
8
|
+
// ROLE: RUNTIME
|
|
9
|
+
// MAP_MODE: EXPORTS
|
|
10
|
+
// END_MODULE_CONTRACT
|
|
11
|
+
//
|
|
12
|
+
// START_MODULE_MAP
|
|
13
|
+
// SecretsRedactionPlugin - main plugin factory function
|
|
14
|
+
// END_MODULE_MAP
|
|
15
|
+
import { loadConfig } from "./config.js";
|
|
16
|
+
import { buildPatternSet } from "./patterns.js";
|
|
17
|
+
import { redactText } from "./engine.js";
|
|
18
|
+
import { restoreText } from "./restore.js";
|
|
19
|
+
import { redactDeep, restoreDeep } from "./deep.js";
|
|
20
|
+
import { PlaceholderSession } from "./session.js";
|
|
21
|
+
const PLACEHOLDER_PREFIX = "__VVOC_SECRET_";
|
|
22
|
+
function isTextPart(part) {
|
|
23
|
+
return part.type === "text";
|
|
24
|
+
}
|
|
25
|
+
function isReasoningPart(part) {
|
|
26
|
+
return part.type === "reasoning";
|
|
27
|
+
}
|
|
28
|
+
function redactMessageParts(parts, patternSet, session) {
|
|
29
|
+
for (const part of parts) {
|
|
30
|
+
if (isTextPart(part)) {
|
|
31
|
+
const result = redactText(part.text, patternSet, session);
|
|
32
|
+
part.text = result.text;
|
|
33
|
+
}
|
|
34
|
+
if (isReasoningPart(part)) {
|
|
35
|
+
const result = redactText(part.text, patternSet, session);
|
|
36
|
+
part.text = result.text;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function redactAssistantState(msg, patternSet, session) {
|
|
41
|
+
const state = msg.state;
|
|
42
|
+
if (state) {
|
|
43
|
+
if (state.input) {
|
|
44
|
+
redactDeep(state.input, patternSet, session);
|
|
45
|
+
}
|
|
46
|
+
if (state.output) {
|
|
47
|
+
redactDeep(state.output, patternSet, session);
|
|
48
|
+
}
|
|
49
|
+
if (state.error) {
|
|
50
|
+
redactDeep(state.error, patternSet, session);
|
|
51
|
+
}
|
|
52
|
+
if (state.raw) {
|
|
53
|
+
redactDeep(state.raw, patternSet, session);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export const SecretsRedactionPlugin = async (ctx) => {
|
|
58
|
+
const { config, path, warnings } = await loadConfig(ctx.directory);
|
|
59
|
+
if (config.debug) {
|
|
60
|
+
await ctx.client.app.log({
|
|
61
|
+
body: {
|
|
62
|
+
service: "secrets-redaction",
|
|
63
|
+
level: "debug",
|
|
64
|
+
message: `config loaded from: ${path ?? "none"}`,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
for (const warning of warnings) {
|
|
69
|
+
await ctx.client.app.log({
|
|
70
|
+
body: {
|
|
71
|
+
service: "secrets-redaction",
|
|
72
|
+
level: "warn",
|
|
73
|
+
message: warning,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (!config.enabled) {
|
|
78
|
+
return {};
|
|
79
|
+
}
|
|
80
|
+
const patternSet = buildPatternSet(config.patterns);
|
|
81
|
+
const session = new PlaceholderSession({
|
|
82
|
+
prefix: PLACEHOLDER_PREFIX,
|
|
83
|
+
ttlMs: config.ttlMs,
|
|
84
|
+
maxMappings: config.maxMappings,
|
|
85
|
+
secret: config.secret,
|
|
86
|
+
});
|
|
87
|
+
if (config.ttlMs > 0) {
|
|
88
|
+
setInterval(() => {
|
|
89
|
+
const evicted = session.cleanup(Date.now());
|
|
90
|
+
if (config.debug && evicted > 0) {
|
|
91
|
+
ctx.client.app.log({
|
|
92
|
+
body: {
|
|
93
|
+
service: "secrets-redaction",
|
|
94
|
+
level: "debug",
|
|
95
|
+
message: `evicted ${evicted} expired placeholders`,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}, Math.min(config.ttlMs, 60_000));
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
config: async () => { },
|
|
103
|
+
event: async () => { },
|
|
104
|
+
"tool.execute.before": async (_input, output) => {
|
|
105
|
+
if (output.args) {
|
|
106
|
+
restoreDeep(output.args, session);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"experimental.chat.messages.transform": async (_input, output) => {
|
|
110
|
+
for (const msg of output.messages) {
|
|
111
|
+
if (msg.info.role === "assistant") {
|
|
112
|
+
redactAssistantState(msg.info, patternSet, session);
|
|
113
|
+
}
|
|
114
|
+
redactMessageParts(msg.parts, patternSet, session);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"experimental.text.complete": async (_input, output) => {
|
|
118
|
+
if (output.text) {
|
|
119
|
+
output.text = restoreText(output.text, session);
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/plugins/secrets-redaction/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface PatternRule {
|
|
2
|
+
pattern: RegExp;
|
|
3
|
+
category: string;
|
|
4
|
+
}
|
|
5
|
+
export interface PatternSet {
|
|
6
|
+
rules: PatternRule[];
|
|
7
|
+
exclude: Set<string>;
|
|
8
|
+
}
|
|
9
|
+
export interface PatternsConfig {
|
|
10
|
+
keywords?: Array<{
|
|
11
|
+
value: string;
|
|
12
|
+
category?: string;
|
|
13
|
+
}>;
|
|
14
|
+
regex?: Array<{
|
|
15
|
+
pattern: string;
|
|
16
|
+
category: string;
|
|
17
|
+
}>;
|
|
18
|
+
builtin?: string[];
|
|
19
|
+
exclude?: string[];
|
|
20
|
+
}
|
|
21
|
+
declare const BUILTIN_PATTERNS: Map<string, {
|
|
22
|
+
pattern: string;
|
|
23
|
+
category: string;
|
|
24
|
+
}>;
|
|
25
|
+
export declare function buildPatternSet(config: PatternsConfig): PatternSet;
|
|
26
|
+
export { BUILTIN_PATTERNS };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// FILE: src/plugins/secrets-redaction/patterns.ts
|
|
2
|
+
// VERSION: 1.0.0
|
|
3
|
+
// START_MODULE_CONTRACT
|
|
4
|
+
// PURPOSE: Builds the internal pattern set from config — keywords, regex rules, and builtin patterns.
|
|
5
|
+
// SCOPE: pattern parsing, normalization, deduplication
|
|
6
|
+
// DEPENDS: node:crypto (for hashing)
|
|
7
|
+
// LINKS: knowledge-graph://plugins/secrets-redaction
|
|
8
|
+
// ROLE: RUNTIME
|
|
9
|
+
// MAP_MODE: EXPORTS
|
|
10
|
+
// END_MODULE_CONTRACT
|
|
11
|
+
//
|
|
12
|
+
// START_MODULE_MAP
|
|
13
|
+
// buildPatternSet - builds pattern set from config object
|
|
14
|
+
// BUILTIN_PATTERNS - Map of 6 builtin pattern definitions
|
|
15
|
+
// END_MODULE_MAP
|
|
16
|
+
const BUILTIN_PATTERNS = new Map([
|
|
17
|
+
["email", { pattern: "[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,}", category: "EMAIL" }],
|
|
18
|
+
[
|
|
19
|
+
"china_phone",
|
|
20
|
+
{ pattern: "(?<!\\d)1[3-9]\\d{9}(?!\\d)", category: "CHINA_PHONE" },
|
|
21
|
+
],
|
|
22
|
+
[
|
|
23
|
+
"china_id",
|
|
24
|
+
{ pattern: "(?<!\\d)\\d{17}[\\dXx](?!\\d)", category: "CHINA_ID" },
|
|
25
|
+
],
|
|
26
|
+
[
|
|
27
|
+
"uuid",
|
|
28
|
+
{
|
|
29
|
+
pattern: "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}",
|
|
30
|
+
category: "UUID",
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
[
|
|
34
|
+
"ipv4",
|
|
35
|
+
{ pattern: "(?:\\d{1,3}\\.){3}\\d{1,3}", category: "IPV4" },
|
|
36
|
+
],
|
|
37
|
+
["mac", { pattern: "(?:[0-9a-f]{2}:){5}[0-9a-f]{2}", category: "MAC" }],
|
|
38
|
+
]);
|
|
39
|
+
function peelFlags(pattern) {
|
|
40
|
+
const inlineFlags = [];
|
|
41
|
+
let p = pattern;
|
|
42
|
+
const iMatch = p.match(/^\(\?([a-z]+)\)/);
|
|
43
|
+
if (iMatch) {
|
|
44
|
+
const captured = iMatch[1];
|
|
45
|
+
if (captured.includes("i"))
|
|
46
|
+
inlineFlags.push("i");
|
|
47
|
+
if (captured.includes("m"))
|
|
48
|
+
inlineFlags.push("m");
|
|
49
|
+
if (captured.includes("s"))
|
|
50
|
+
inlineFlags.push("s");
|
|
51
|
+
p = p.slice(iMatch[0].length);
|
|
52
|
+
}
|
|
53
|
+
return { pattern: p, flags: inlineFlags.join("") };
|
|
54
|
+
}
|
|
55
|
+
function buildRegex(pattern, defaultFlags = "gi") {
|
|
56
|
+
const { pattern: raw, flags: peeled } = peelFlags(pattern);
|
|
57
|
+
const flags = peeled ? `${defaultFlags}${peeled}` : defaultFlags;
|
|
58
|
+
return new RegExp(raw, flags);
|
|
59
|
+
}
|
|
60
|
+
export function buildPatternSet(config) {
|
|
61
|
+
const rules = [];
|
|
62
|
+
if (config.builtin) {
|
|
63
|
+
for (const name of config.builtin) {
|
|
64
|
+
const builtin = BUILTIN_PATTERNS.get(name);
|
|
65
|
+
if (builtin) {
|
|
66
|
+
rules.push({ pattern: buildRegex(builtin.pattern), category: builtin.category });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (config.regex) {
|
|
71
|
+
for (const { pattern, category } of config.regex) {
|
|
72
|
+
rules.push({ pattern: buildRegex(pattern), category });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (config.keywords) {
|
|
76
|
+
for (const { value, category = "KEYWORD" } of config.keywords) {
|
|
77
|
+
const escaped = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
78
|
+
rules.push({ pattern: new RegExp(escaped, "gi"), category });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const exclude = new Set(config.exclude ?? []);
|
|
82
|
+
return { rules, exclude };
|
|
83
|
+
}
|
|
84
|
+
export { BUILTIN_PATTERNS };
|
|
85
|
+
//# sourceMappingURL=patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../../src/plugins/secrets-redaction/patterns.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// FILE: src/plugins/secrets-redaction/restore.ts
|
|
2
|
+
// VERSION: 1.0.0
|
|
3
|
+
// START_MODULE_CONTRACT
|
|
4
|
+
// PURPOSE: Restores placeholders in a single string back to original secret values.
|
|
5
|
+
// SCOPE: single-string placeholder → original lookup
|
|
6
|
+
// DEPENDS: session
|
|
7
|
+
// LINKS: knowledge-graph://plugins/secrets-redaction
|
|
8
|
+
// ROLE: RUNTIME
|
|
9
|
+
// MAP_MODE: EXPORTS
|
|
10
|
+
// END_MODULE_CONTRACT
|
|
11
|
+
//
|
|
12
|
+
// START_MODULE_MAP
|
|
13
|
+
// restoreText - restores all placeholders in a string to original values
|
|
14
|
+
// END_MODULE_MAP
|
|
15
|
+
import { getPlaceholderRegex } from "./session.js";
|
|
16
|
+
export function restoreText(input, session) {
|
|
17
|
+
if (!input)
|
|
18
|
+
return input;
|
|
19
|
+
const regex = getPlaceholderRegex(session["prefix"]);
|
|
20
|
+
return input.replace(regex, (placeholder) => {
|
|
21
|
+
const original = session.lookup(placeholder);
|
|
22
|
+
return original ?? placeholder;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=restore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restore.js","sourceRoot":"","sources":["../../../src/plugins/secrets-redaction/restore.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,iBAAiB;AACjB,wBAAwB;AACxB,sFAAsF;AACtF,uDAAuD;AACvD,qBAAqB;AACrB,uDAAuD;AACvD,kBAAkB;AAClB,sBAAsB;AACtB,sBAAsB;AACtB,EAAE;AACF,mBAAmB;AACnB,2EAA2E;AAC3E,iBAAiB;AAGjB,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,OAA2B;IACpE,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IAEzB,MAAM,KAAK,GAAG,mBAAmB,CAAC,OAAO,CAAC,QAAQ,CAAsB,CAAC,CAAC;IAC1E,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,EAAE;QAC1C,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAC7C,OAAO,QAAQ,IAAI,WAAW,CAAC;IACjC,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface PlaceholderSessionOptions {
|
|
2
|
+
prefix: string;
|
|
3
|
+
ttlMs: number;
|
|
4
|
+
maxMappings: number;
|
|
5
|
+
secret: string;
|
|
6
|
+
}
|
|
7
|
+
export interface PlaceholderEntry {
|
|
8
|
+
original: string;
|
|
9
|
+
placeholder: string;
|
|
10
|
+
category: string;
|
|
11
|
+
createdAt: number;
|
|
12
|
+
}
|
|
13
|
+
export declare class PlaceholderSession {
|
|
14
|
+
private readonly prefix;
|
|
15
|
+
private readonly ttlMs;
|
|
16
|
+
private readonly maxMappings;
|
|
17
|
+
private readonly secret;
|
|
18
|
+
private readonly forward;
|
|
19
|
+
private readonly reverse;
|
|
20
|
+
private readonly created;
|
|
21
|
+
constructor(options: PlaceholderSessionOptions);
|
|
22
|
+
private computeHash;
|
|
23
|
+
getOrCreatePlaceholder(original: string, category: string): string;
|
|
24
|
+
lookup(placeholder: string): string | undefined;
|
|
25
|
+
cleanup(now: number): number;
|
|
26
|
+
private evictIfNeeded;
|
|
27
|
+
get size(): number;
|
|
28
|
+
}
|
|
29
|
+
export declare function getPlaceholderRegex(prefix: string): RegExp;
|
|
30
|
+
export declare function generateFallbackSecret(): string;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// FILE: src/plugins/secrets-redaction/session.ts
|
|
2
|
+
// VERSION: 1.0.0
|
|
3
|
+
// START_MODULE_CONTRACT
|
|
4
|
+
// PURPOSE: Manages placeholder lifecycle for secret redaction — generates stable HMAC-based placeholders,
|
|
5
|
+
// maintains forward/reverse mappings, handles TTL eviction and max-mappings limits.
|
|
6
|
+
// SCOPE: placeholder creation, lookup, cleanup
|
|
7
|
+
// DEPENDS: node:crypto
|
|
8
|
+
// LINKS: knowledge-graph://plugins/secrets-redaction
|
|
9
|
+
// ROLE: RUNTIME
|
|
10
|
+
// MAP_MODE: EXPORTS
|
|
11
|
+
// END_MODULE_CONTRACT
|
|
12
|
+
//
|
|
13
|
+
// START_MODULE_MAP
|
|
14
|
+
// PlaceholderSession - manages secret → placeholder mappings with HMAC hashing
|
|
15
|
+
// getPlaceholderRegex - returns RegExp to find all placeholders in text
|
|
16
|
+
// END_MODULE_MAP
|
|
17
|
+
import { createHmac, randomBytes } from "node:crypto";
|
|
18
|
+
export class PlaceholderSession {
|
|
19
|
+
prefix;
|
|
20
|
+
ttlMs;
|
|
21
|
+
maxMappings;
|
|
22
|
+
secret;
|
|
23
|
+
forward = new Map();
|
|
24
|
+
reverse = new Map();
|
|
25
|
+
created = new Map();
|
|
26
|
+
constructor(options) {
|
|
27
|
+
this.prefix = options.prefix;
|
|
28
|
+
this.ttlMs = options.ttlMs;
|
|
29
|
+
this.maxMappings = options.maxMappings;
|
|
30
|
+
this.secret = Buffer.from(options.secret, "utf-8");
|
|
31
|
+
}
|
|
32
|
+
computeHash(original) {
|
|
33
|
+
return createHmac("sha256", this.secret).update(original, "utf-8").digest("hex");
|
|
34
|
+
}
|
|
35
|
+
getOrCreatePlaceholder(original, category) {
|
|
36
|
+
if (this.reverse.has(original)) {
|
|
37
|
+
return this.reverse.get(original);
|
|
38
|
+
}
|
|
39
|
+
this.evictIfNeeded();
|
|
40
|
+
const hash = this.computeHash(original);
|
|
41
|
+
const hash12 = hash.substring(0, 12);
|
|
42
|
+
let placeholder = `${this.prefix}${category}_${hash12}__`;
|
|
43
|
+
if (this.forward.has(placeholder)) {
|
|
44
|
+
let counter = 1;
|
|
45
|
+
while (this.forward.has(placeholder)) {
|
|
46
|
+
placeholder = `${this.prefix}${category}_${hash12}_${counter}__`;
|
|
47
|
+
counter++;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const entry = {
|
|
51
|
+
original,
|
|
52
|
+
placeholder,
|
|
53
|
+
category,
|
|
54
|
+
createdAt: Date.now(),
|
|
55
|
+
};
|
|
56
|
+
this.forward.set(placeholder, entry);
|
|
57
|
+
this.reverse.set(original, placeholder);
|
|
58
|
+
this.created.set(placeholder, Date.now());
|
|
59
|
+
return placeholder;
|
|
60
|
+
}
|
|
61
|
+
lookup(placeholder) {
|
|
62
|
+
return this.forward.get(placeholder)?.original;
|
|
63
|
+
}
|
|
64
|
+
cleanup(now) {
|
|
65
|
+
let evicted = 0;
|
|
66
|
+
const expired = [];
|
|
67
|
+
for (const [placeholder, createdAt] of this.created) {
|
|
68
|
+
if (now - createdAt > this.ttlMs) {
|
|
69
|
+
expired.push(placeholder);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
for (const placeholder of expired) {
|
|
73
|
+
const entry = this.forward.get(placeholder);
|
|
74
|
+
if (entry) {
|
|
75
|
+
this.reverse.delete(entry.original);
|
|
76
|
+
this.forward.delete(placeholder);
|
|
77
|
+
this.created.delete(placeholder);
|
|
78
|
+
evicted++;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return evicted;
|
|
82
|
+
}
|
|
83
|
+
evictIfNeeded() {
|
|
84
|
+
if (this.forward.size < this.maxMappings)
|
|
85
|
+
return;
|
|
86
|
+
let oldestPlaceholder = null;
|
|
87
|
+
let oldestCreated = Infinity;
|
|
88
|
+
for (const [placeholder, createdAt] of this.created) {
|
|
89
|
+
if (createdAt < oldestCreated) {
|
|
90
|
+
oldestCreated = createdAt;
|
|
91
|
+
oldestPlaceholder = placeholder;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (oldestPlaceholder) {
|
|
95
|
+
const entry = this.forward.get(oldestPlaceholder);
|
|
96
|
+
if (entry) {
|
|
97
|
+
this.reverse.delete(entry.original);
|
|
98
|
+
this.forward.delete(oldestPlaceholder);
|
|
99
|
+
this.created.delete(oldestPlaceholder);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
get size() {
|
|
104
|
+
return this.forward.size;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export function getPlaceholderRegex(prefix) {
|
|
108
|
+
return new RegExp(`${prefix}[A-Z_]+_[0-9a-f]{12}(?:_\\d+)?__`, "g");
|
|
109
|
+
}
|
|
110
|
+
export function generateFallbackSecret() {
|
|
111
|
+
return randomBytes(32).toString("hex");
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/plugins/secrets-redaction/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SecretsRedactionPlugin } from "./secrets-redaction/index.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// FILE: src/plugins/secrets-redaction.ts
|
|
2
|
+
// VERSION: 1.0.0
|
|
3
|
+
// START_MODULE_CONTRACT
|
|
4
|
+
// PURPOSE: Re-export barrel for SecretsRedactionPlugin
|
|
5
|
+
// SCOPE: plugin re-exports
|
|
6
|
+
// DEPENDS: secrets-redaction/index
|
|
7
|
+
// LINKS: knowledge-graph://plugins/secrets-redaction
|
|
8
|
+
// ROLE: BARREL
|
|
9
|
+
// MAP_MODE: SUMMARY
|
|
10
|
+
// END_MODULE_CONTRACT
|
|
11
|
+
//
|
|
12
|
+
// START_MODULE_MAP
|
|
13
|
+
// SecretsRedactionPlugin - main plugin export
|
|
14
|
+
// END_MODULE_MAP
|
|
15
|
+
export { SecretsRedactionPlugin } from "./secrets-redaction/index.js";
|
|
16
|
+
//# sourceMappingURL=secrets-redaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets-redaction.js","sourceRoot":"","sources":["../../src/plugins/secrets-redaction.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,iBAAiB;AACjB,wBAAwB;AACxB,yDAAyD;AACzD,6BAA6B;AAC7B,qCAAqC;AACrC,uDAAuD;AACvD,iBAAiB;AACjB,sBAAsB;AACtB,sBAAsB;AACtB,EAAE;AACF,mBAAmB;AACnB,gDAAgD;AAChD,iBAAiB;AAEjB,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@osovv/vv-opencode",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Portable OpenCode workflow plugins and CLI tooling.",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Portable OpenCode workflow plugins, explicit memory, and CLI tooling.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"workflow",
|
|
16
16
|
"guardian",
|
|
17
17
|
"memory",
|
|
18
|
+
"xdg",
|
|
19
|
+
"grace",
|
|
18
20
|
"bun",
|
|
19
21
|
"citty"
|
|
20
22
|
],
|
|
@@ -47,6 +49,7 @@
|
|
|
47
49
|
"fmt:check": "oxfmt --check src",
|
|
48
50
|
"test": "bun test",
|
|
49
51
|
"check": "bun run typecheck && bun run lint && bun run fmt:check && bun test",
|
|
52
|
+
"pack:check": "npm pack --dry-run",
|
|
50
53
|
"prepare": "lefthook install --force"
|
|
51
54
|
},
|
|
52
55
|
"dependencies": {
|