@rune-kit/rune 2.4.0 → 2.6.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.
@@ -1,68 +1,177 @@
1
- // Rune Pre-Tool Guard Hook
2
- // Preventive security gate warns before AI reads/edits sensitive files
3
- // Runs as PreToolUse hook for Read, Write, Edit tools
4
-
5
- const path = require('path');
6
-
7
- // Read tool_input from Claude Code hook stdin
8
- let input = '';
9
- process.stdin.setEncoding('utf-8');
10
- process.stdin.on('data', (chunk) => { input += chunk; });
11
- process.stdin.on('end', () => {
12
- let toolInput = {};
13
- try {
14
- const parsed = JSON.parse(input);
15
- toolInput = parsed.tool_input || parsed;
16
- } catch {
17
- // If no stdin or parse fails, exit cleanly (non-blocking)
18
- process.exit(0);
19
- }
20
-
21
- const filePath = toolInput.file_path || toolInput.path || '';
22
- if (!filePath) process.exit(0);
23
-
24
- const basename = path.basename(filePath);
25
- const normalized = filePath.replace(/\\/g, '/');
26
-
27
- // Sensitive file patterns
28
- const sensitivePatterns = [
29
- /^\.env$/, // .env exactly
30
- /^\.env\.[^e]/, // .env.* but NOT .env.example
31
- /\.pem$/,
32
- /\.key$/,
33
- /\.p12$/,
34
- /\.pfx$/,
35
- /^credentials\.json$/,
36
- /\.secret$/,
37
- /^\.netrc$/,
38
- /^id_rsa$/,
39
- /^id_ed25519$/,
40
- /^id_ecdsa$/,
41
- /^\.ssh\//,
42
- /private.*key/i,
43
- /secret.*key/i,
44
- ];
45
-
46
- // Exclude safe exceptions
47
- const safeExceptions = [
48
- /\.env\.example$/,
49
- /\.env\.sample$/,
50
- /\.env\.template$/,
51
- /\.env\.test$/,
52
- /test.*credential/i,
53
- /fixture/i,
54
- /mock/i,
55
- ];
56
-
57
- const isSensitive = sensitivePatterns.some((p) => p.test(basename) || p.test(normalized));
58
- const isSafe = safeExceptions.some((p) => p.test(basename) || p.test(normalized));
59
-
60
- if (isSensitive && !isSafe) {
61
- // Output warning to user — does NOT hard-block (developer may legitimately need access)
62
- console.log(`\n⚠ [Rune pre-tool-guard] Sensitive file detected: ${filePath}`);
63
- console.log(' This file may contain secrets. Confirm this access is intentional.');
64
- console.log(' If scanning for secrets, use /rune sentinel instead.\n');
65
- }
66
-
67
- process.exit(0);
68
- });
1
+ // Rune Pre-Tool Guard Hook — Privacy Mesh
2
+ // Three-tier security gate: ALLOW / WARN / BLOCK
3
+ // Configurable via .rune/privacy.json per project
4
+ // Skill-aware: sentinel/review can access files other skills cannot
5
+ //
6
+ // Upgrades over basic path matching:
7
+ // - Per-project .rune/privacy.json config (custom patterns + overrides)
8
+ // - Three tiers: ALLOW (silent), WARN (log), BLOCK (exit code 2)
9
+ // - Content-aware: scans first bytes for secret patterns on WARN files
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+
14
+ // Read tool_input from Claude Code hook stdin
15
+ let input = '';
16
+ process.stdin.setEncoding('utf-8');
17
+ process.stdin.on('data', (chunk) => { input += chunk; });
18
+ process.stdin.on('end', () => {
19
+ let toolInput = {};
20
+ let toolName = '';
21
+ try {
22
+ const parsed = JSON.parse(input);
23
+ toolInput = parsed.tool_input || parsed;
24
+ toolName = parsed.tool_name || '';
25
+ } catch {
26
+ // If no stdin or parse fails, exit cleanly (non-blocking)
27
+ process.exit(0);
28
+ }
29
+
30
+ const filePath = toolInput.file_path || toolInput.path || '';
31
+ if (!filePath) process.exit(0);
32
+
33
+ const basename = path.basename(filePath);
34
+ const normalized = filePath.replace(/\\/g, '/');
35
+
36
+ // Load project-specific privacy config
37
+ const privacyConfig = loadPrivacyConfig();
38
+
39
+ // Default sensitive file patterns — BLOCK tier (hard block)
40
+ const blockPatterns = [
41
+ /^id_rsa$/,
42
+ /^id_ed25519$/,
43
+ /^id_ecdsa$/,
44
+ /\.p12$/,
45
+ /\.pfx$/,
46
+ /\.pem$/,
47
+ /^\.netrc$/,
48
+ /private[-_.]?key/i,
49
+ ...privacyConfig.block.map((p) => new RegExp(p)),
50
+ ];
51
+
52
+ // WARN tier (log warning, allow through)
53
+ const warnPatterns = [
54
+ /^\.env$/,
55
+ /^\.env\.[^e]/, // .env.* but NOT .env.example
56
+ /\.key$/,
57
+ /^credentials\.json$/,
58
+ /\.secret$/,
59
+ /secret[-_.]?key/i,
60
+ ...privacyConfig.warn.map((p) => new RegExp(p)),
61
+ ];
62
+
63
+ // Safe exceptions (always allow)
64
+ const safeExceptions = [
65
+ /\.env\.example$/,
66
+ /\.env\.sample$/,
67
+ /\.env\.template$/,
68
+ /\.env\.test$/,
69
+ /test.*credential/i,
70
+ /fixture/i,
71
+ /mock/i,
72
+ ...privacyConfig.allow.map((p) => new RegExp(p)),
73
+ ];
74
+
75
+ // Skills with elevated access (can read WARN-tier files without warning)
76
+ const elevatedSkills = new Set([
77
+ 'sentinel', 'review', 'audit', 'secrets-scan', 'onboard',
78
+ ...privacyConfig.elevatedSkills,
79
+ ]);
80
+
81
+ // Detect active skill from environment (set by metrics-collector or skill invocation)
82
+ const activeSkill = process.env.RUNE_ACTIVE_SKILL || '';
83
+ const isElevated = elevatedSkills.has(activeSkill);
84
+
85
+ const isSafe = safeExceptions.some((p) => p.test(basename) || p.test(normalized));
86
+ if (isSafe) process.exit(0);
87
+
88
+ const isBlocked = blockPatterns.some((p) => p.test(basename) || p.test(normalized));
89
+ if (isBlocked) {
90
+ console.log(`\n🚫 [Rune privacy-mesh] BLOCKED: ${filePath}`);
91
+ console.log(' This file matches a BLOCK-tier pattern (private keys, certificates).');
92
+ console.log(' Override: add path to .rune/privacy.json "allow" list if intentional.\n');
93
+ process.exit(2); // Exit code 2 = BLOCK
94
+ }
95
+
96
+ const isWarned = warnPatterns.some((p) => p.test(basename) || p.test(normalized));
97
+ if (isWarned && !isElevated) {
98
+ // Content-aware check: scan first 4KB for secret patterns
99
+ const contentWarning = scanContentForSecrets(filePath);
100
+
101
+ console.log(`\n⚠ [Rune privacy-mesh] Sensitive file: ${filePath}`);
102
+ if (contentWarning) {
103
+ console.log(` Content scan: ${contentWarning}`);
104
+ }
105
+ console.log(' This file may contain secrets. Confirm access is intentional.');
106
+ console.log(' Elevated skills (sentinel, review, audit) bypass this warning.\n');
107
+ }
108
+
109
+ process.exit(0);
110
+ });
111
+
112
+ /**
113
+ * Load .rune/privacy.json from project root
114
+ * Returns merged config with defaults
115
+ */
116
+ function loadPrivacyConfig() {
117
+ const defaults = { block: [], warn: [], allow: [], elevatedSkills: [] };
118
+
119
+ const candidates = [
120
+ path.join(process.cwd(), '.rune', 'privacy.json'),
121
+ path.join(process.cwd(), 'privacy.json'),
122
+ ];
123
+
124
+ for (const configPath of candidates) {
125
+ try {
126
+ if (fs.existsSync(configPath)) {
127
+ const raw = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
128
+ return {
129
+ block: Array.isArray(raw.block) ? raw.block : defaults.block,
130
+ warn: Array.isArray(raw.warn) ? raw.warn : defaults.warn,
131
+ allow: Array.isArray(raw.allow) ? raw.allow : defaults.allow,
132
+ elevatedSkills: Array.isArray(raw.elevatedSkills) ? raw.elevatedSkills : defaults.elevatedSkills,
133
+ };
134
+ }
135
+ } catch {
136
+ // Invalid config — use defaults
137
+ }
138
+ }
139
+
140
+ return defaults;
141
+ }
142
+
143
+ /**
144
+ * Scan first 4KB of a file for common secret patterns
145
+ * Returns a warning string if secrets detected, null otherwise
146
+ */
147
+ function scanContentForSecrets(filePath) {
148
+ try {
149
+ const fd = fs.openSync(filePath, 'r');
150
+ const buffer = Buffer.alloc(4096);
151
+ const bytesRead = fs.readSync(fd, buffer, 0, 4096, 0);
152
+ fs.closeSync(fd);
153
+
154
+ if (bytesRead === 0) return null;
155
+
156
+ const content = buffer.toString('utf-8', 0, bytesRead);
157
+
158
+ const secretPatterns = [
159
+ { pattern: /AKIA[0-9A-Z]{16}/, label: 'AWS access key' },
160
+ { pattern: /gh[ps]_[A-Za-z0-9_]{36,}/, label: 'GitHub token' },
161
+ { pattern: /sk_(live|test)_[0-9a-zA-Z]{24,}/, label: 'Stripe key' },
162
+ { pattern: /-----BEGIN .* PRIVATE KEY-----/, label: 'Private key' },
163
+ { pattern: /xox[bpors]-[0-9a-zA-Z-]{10,}/, label: 'Slack token' },
164
+ { pattern: /sk-[a-zA-Z0-9]{32,}/, label: 'OpenAI/API key' },
165
+ ];
166
+
167
+ for (const { pattern, label } of secretPatterns) {
168
+ if (pattern.test(content)) {
169
+ return `Found ${label} pattern in file content`;
170
+ }
171
+ }
172
+ } catch {
173
+ // Can't read file (doesn't exist yet, permissions, etc.) — skip silently
174
+ }
175
+
176
+ return null;
177
+ }
package/package.json CHANGED
@@ -1,63 +1,63 @@
1
- {
2
- "name": "@rune-kit/rune",
3
- "version": "2.4.0",
4
- "description": "60-skill mesh for AI coding assistants — 5-layer architecture, 200+ connections, 8 platforms (Claude Code, Cursor, Windsurf, Antigravity, Codex, OpenCode, OpenClaw, Generic)",
5
- "type": "module",
6
- "bin": {
7
- "rune": "./compiler/bin/rune.js"
8
- },
9
- "scripts": {
10
- "build": "node compiler/bin/rune.js build",
11
- "doctor": "node compiler/bin/rune.js doctor && node scripts/version-sync-check.js",
12
- "test": "node --test compiler/__tests__/*.test.js scripts/__tests__/*.test.js",
13
- "test:coverage": "c8 --reporter=text --reporter=lcov node --test compiler/__tests__/*.test.js scripts/__tests__/*.test.js",
14
- "lint": "biome check .",
15
- "lint:fix": "biome check --fix .",
16
- "format": "biome format --write .",
17
- "ci": "biome check . && node --test compiler/__tests__/*.test.js scripts/__tests__/*.test.js && node compiler/bin/rune.js doctor",
18
- "version-check": "node scripts/version-sync-check.js",
19
- "prepublishOnly": "node scripts/version-sync-check.js"
20
- },
21
- "keywords": [
22
- "claude-code",
23
- "cursor",
24
- "windsurf",
25
- "antigravity",
26
- "codex",
27
- "opencode",
28
- "openclaw",
29
- "ai-assistant",
30
- "ai-coding",
31
- "skills",
32
- "coding-agent",
33
- "tdd",
34
- "multi-platform"
35
- ],
36
- "author": "runedev",
37
- "license": "MIT",
38
- "repository": {
39
- "type": "git",
40
- "url": "https://github.com/rune-kit/rune"
41
- },
42
- "engines": {
43
- "node": ">=18"
44
- },
45
- "files": [
46
- "compiler/",
47
- "skills/",
48
- "extensions/",
49
- "contexts/",
50
- "commands/",
51
- "agents/",
52
- "hooks/",
53
- "references/"
54
- ],
55
- "homepage": "https://rune-kit.github.io/rune",
56
- "bugs": {
57
- "url": "https://github.com/rune-kit/rune/issues"
58
- },
59
- "devDependencies": {
60
- "@biomejs/biome": "^2.4.7",
61
- "c8": "^10.1.3"
62
- }
63
- }
1
+ {
2
+ "name": "@rune-kit/rune",
3
+ "version": "2.6.0",
4
+ "description": "61-skill mesh for AI coding assistants — 5-layer architecture, 200+ connections, 8 platforms (Claude Code, Cursor, Windsurf, Antigravity, Codex, OpenCode, OpenClaw, Generic)",
5
+ "type": "module",
6
+ "bin": {
7
+ "rune": "./compiler/bin/rune.js"
8
+ },
9
+ "scripts": {
10
+ "build": "node compiler/bin/rune.js build",
11
+ "doctor": "node compiler/bin/rune.js doctor && node scripts/version-sync-check.js",
12
+ "test": "node --test compiler/__tests__/*.test.js scripts/__tests__/*.test.js",
13
+ "test:coverage": "c8 --reporter=text --reporter=lcov node --test compiler/__tests__/*.test.js scripts/__tests__/*.test.js",
14
+ "lint": "biome check .",
15
+ "lint:fix": "biome check --fix .",
16
+ "format": "biome format --write .",
17
+ "ci": "biome check . && node --test compiler/__tests__/*.test.js scripts/__tests__/*.test.js && node compiler/bin/rune.js doctor",
18
+ "version-check": "node scripts/version-sync-check.js",
19
+ "prepublishOnly": "node scripts/version-sync-check.js"
20
+ },
21
+ "keywords": [
22
+ "claude-code",
23
+ "cursor",
24
+ "windsurf",
25
+ "antigravity",
26
+ "codex",
27
+ "opencode",
28
+ "openclaw",
29
+ "ai-assistant",
30
+ "ai-coding",
31
+ "skills",
32
+ "coding-agent",
33
+ "tdd",
34
+ "multi-platform"
35
+ ],
36
+ "author": "runedev",
37
+ "license": "MIT",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://github.com/rune-kit/rune"
41
+ },
42
+ "engines": {
43
+ "node": ">=18"
44
+ },
45
+ "files": [
46
+ "compiler/",
47
+ "skills/",
48
+ "extensions/",
49
+ "contexts/",
50
+ "commands/",
51
+ "agents/",
52
+ "hooks/",
53
+ "references/"
54
+ ],
55
+ "homepage": "https://rune-kit.github.io/rune",
56
+ "bugs": {
57
+ "url": "https://github.com/rune-kit/rune/issues"
58
+ },
59
+ "devDependencies": {
60
+ "@biomejs/biome": "^2.4.7",
61
+ "c8": "^10.1.3"
62
+ }
63
+ }
@@ -8,6 +8,8 @@ metadata:
8
8
  model: opus
9
9
  group: creation
10
10
  tools: "Read, Glob, Grep"
11
+ emit: ideas.ready
12
+ listen: codebase.scanned
11
13
  ---
12
14
 
13
15
  # brainstorm