hackmyagent 0.11.11 → 0.11.12

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 CHANGED
@@ -5,7 +5,7 @@
5
5
  [![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
6
6
  [![Tests](https://img.shields.io/badge/tests-1051%20passing-brightgreen)](https://github.com/opena2a-org/hackmyagent)
7
7
 
8
- **199 security checks for AI agents. Find what can go wrong before an attacker does.**
8
+ **204 security checks for AI agents. Find what can go wrong before an attacker does.**
9
9
 
10
10
  Security scanner and red-team toolkit for Claude Code, Cursor, VS Code, and any MCP server setup.
11
11
 
@@ -31,7 +31,7 @@ npx opena2a-cli review
31
31
 
32
32
  **Attack testing** -- 115 adversarial payloads across 11 categories (prompt injection, data exfiltration, jailbreak, MCP exploitation, supply chain, memory weaponization, A2A protocol attacks, context window attacks).
33
33
 
34
- **Static analysis** -- 199 security checks across 60 categories covering credentials, MCP configs, OpenClaw/NemoClaw, Unicode steganography, CVE detection, governance, supply chain, memory poisoning, agent identity, and sandbox escape patterns.
34
+ **Static analysis** -- 204 security checks across 60 categories covering credentials, MCP configs, OpenClaw/NemoClaw, Unicode steganography, CVE detection, governance, supply chain, memory poisoning, agent identity, and sandbox escape patterns.
35
35
 
36
36
  <details>
37
37
  <summary>Attack testing details (115 payloads)</summary>
@@ -49,7 +49,7 @@ npx opena2a-cli review
49
49
  </details>
50
50
 
51
51
  <details>
52
- <summary>Static analysis details (199 checks)</summary>
52
+ <summary>Static analysis details (204 checks)</summary>
53
53
 
54
54
  - **Unicode steganography** -- invisible codepoints, zero-width chars, bidi attacks, homoglyph confusables, GlassWorm decoders ([real-world: os-info-checker-es6 npm attack, May 2025](https://thehackernews.com/2025/05/malicious-npm-package-leverages-unicode.html))
55
55
  - **Hardcoded credentials** -- API keys, tokens, and passwords in source or config files
@@ -65,7 +65,7 @@ npx opena2a-cli review
65
65
 
66
66
  </details>
67
67
 
68
- 199 checks across 60 categories. 115 attack payloads. No flags needed.
68
+ 204 checks across 60 categories. 115 attack payloads. No flags needed.
69
69
 
70
70
  ---
71
71
 
@@ -107,7 +107,7 @@ npm install --save-dev hackmyagent
107
107
 
108
108
  Step-by-step guides for common workflows:
109
109
 
110
- - **[Scan my agent](docs/use-cases/scan-my-agent.md)** -- Run all 199 checks and auto-fix findings (5 min)
110
+ - **[Scan my agent](docs/use-cases/scan-my-agent.md)** -- Run all 204 checks and auto-fix findings (5 min)
111
111
  - **[Red-team MCP servers](docs/use-cases/red-team-mcp.md)** -- Test MCP servers with adversarial payloads (10 min)
112
112
  - **[Secure OpenClaw](docs/use-cases/openclaw-security.md)** -- Auto-detected when OpenClaw files are present. Includes CVE detection and ClawHavoc IOC scanning (10 min)
113
113
  - **[CI/CD pipeline](docs/use-cases/ci-pipeline.md)** -- GitHub Actions with JSON/SARIF output (5 min)
@@ -281,7 +281,7 @@ hackmyagent harden-soul --dry-run # preview without writing
281
281
 
282
282
  ### OpenClaw and NemoClaw Detection
283
283
 
284
- `hackmyagent secure` auto-detects OpenClaw and NemoClaw installations by looking for `.openclaw/`, `.moltbot/`, `.nemoclaw/`, `openclaw.json`, and `openclaw.plugin.json`. When detected, it automatically runs platform-specific checks (28 NemoClaw checks, 34 OpenClaw checks) alongside the standard 199 security checks. No separate commands needed.
284
+ `hackmyagent secure` auto-detects OpenClaw and NemoClaw installations by looking for `.openclaw/`, `.moltbot/`, `.nemoclaw/`, `openclaw.json`, and `openclaw.plugin.json`. When detected, it automatically runs platform-specific checks (28 NemoClaw checks, 34 OpenClaw checks) alongside the standard 204 security checks. No separate commands needed.
285
285
 
286
286
 
287
287
  ---
package/dist/cli.js CHANGED
@@ -118,7 +118,7 @@ program
118
118
  .name('hackmyagent')
119
119
  .description(`Find it. Break it. Fix it.
120
120
 
121
- The hacker's toolkit for AI agents. 199 security checks, 115 attack
121
+ The hacker's toolkit for AI agents. 204 security checks, 115 attack
122
122
  payloads, auto-fix with rollback, and OASB benchmark compliance.
123
123
 
124
124
  Documentation: https://hackmyagent.com/docs
@@ -127,10 +127,10 @@ Updates (v${index_1.VERSION}):
127
127
  - NemoClaw sandbox scanner (28 installation checks)
128
128
  - 10 new static analysis patterns (NEMO series)
129
129
  - Community trust contributions
130
- - 199 checks across 60 categories
130
+ - 204 checks across 60 categories
131
131
 
132
132
  Examples:
133
- $ hackmyagent secure Find vulnerabilities (199 checks)
133
+ $ hackmyagent secure Find vulnerabilities (204 checks)
134
134
  $ hackmyagent attack --local Break it with 115 attack payloads
135
135
  $ hackmyagent secure --fix Fix issues automatically
136
136
  $ hackmyagent fix-all Run all security plugins
@@ -139,7 +139,7 @@ Examples:
139
139
  .option('--no-color', 'Disable colored output (also respects NO_COLOR env)');
140
140
  program.addHelpText('beforeAll', `
141
141
  Quick start:
142
- $ hackmyagent secure Scan current directory (199 checks)
142
+ $ hackmyagent secure Scan current directory (204 checks)
143
143
  $ hackmyagent fix-all --with-aim Auto-fix + create agent identity
144
144
  $ hackmyagent attack Red-team your agent
145
145
  `);
@@ -1695,7 +1695,7 @@ program
1695
1695
  .command('secure')
1696
1696
  .description(`Scan and harden your agent setup
1697
1697
 
1698
- Performs 199 security checks across 60 categories:
1698
+ Performs 204 security checks across 60 categories:
1699
1699
  • Credentials: API key exposure, secrets in configs
1700
1700
  • MCP: Server configs, tool permissions, secrets
1701
1701
  • Network: TLS, interface bindings, CORS
@@ -4303,7 +4303,7 @@ Examples:
4303
4303
  console.log(`\n Detected: ${result.tool}\n`);
4304
4304
  console.log(` Added HackMyAgent MCP server to ${result.configPath}\n`);
4305
4305
  console.log(` Available tools in ${result.tool}:`);
4306
- console.log(` hackmyagent_scan — 199 checks + structural analysis`);
4306
+ console.log(` hackmyagent_scan — 204 checks + structural analysis`);
4307
4307
  console.log(` hackmyagent_deep_scan — Full analysis with LLM reasoning`);
4308
4308
  console.log(` hackmyagent_analyze_file — Analyze a single file`);
4309
4309
  console.log(` hackmyagent_benchmark — OASB-1 compliance assessment\n`);
@@ -123,7 +123,7 @@ export declare class HardeningScanner {
123
123
  */
124
124
  private findSkillFiles;
125
125
  /**
126
- * OpenClaw skill security checks (SKILL-001 to SKILL-006)
126
+ * OpenClaw skill security checks (SKILL-001 to SKILL-024)
127
127
  */
128
128
  private checkOpenclawSkills;
129
129
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/hardening/scanner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/hardening/scanner.ts"],"names":[],"mappings":"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"}
@@ -4105,7 +4105,7 @@ dist/
4105
4105
  return skillFiles;
4106
4106
  }
4107
4107
  /**
4108
- * OpenClaw skill security checks (SKILL-001 to SKILL-006)
4108
+ * OpenClaw skill security checks (SKILL-001 to SKILL-024)
4109
4109
  */
4110
4110
  async checkOpenclawSkills(targetDir, autoFix) {
4111
4111
  const findings = [];
@@ -4543,6 +4543,197 @@ dist/
4543
4543
  }
4544
4544
  }
4545
4545
  }
4546
+ // SKILL-020: Missing/invalid frontmatter
4547
+ const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
4548
+ if (!fmMatch) {
4549
+ findings.push({
4550
+ checkId: 'SKILL-020',
4551
+ name: 'Missing YAML Frontmatter',
4552
+ description: 'Skill file lacks required YAML frontmatter for capability declaration',
4553
+ category: 'skill',
4554
+ severity: 'high',
4555
+ passed: false,
4556
+ message: `${relativePath}: Skill file lacks required YAML frontmatter (---). Add frontmatter with name, version, and capabilities fields.`,
4557
+ file: relativePath,
4558
+ fixable: true,
4559
+ fix: 'Add YAML frontmatter block with name, version, and capabilities fields',
4560
+ guidance: 'Skills without frontmatter cannot declare their capabilities, making permission validation impossible. Add a --- delimited YAML block at the top of the file.',
4561
+ });
4562
+ }
4563
+ else {
4564
+ const fmRaw = fmMatch[1];
4565
+ const requiredFields = ['name', 'version', 'capabilities'];
4566
+ const missingFields = requiredFields.filter(f => !new RegExp(`^${f}:`, 'm').test(fmRaw));
4567
+ if (missingFields.length > 0) {
4568
+ findings.push({
4569
+ checkId: 'SKILL-020',
4570
+ name: 'Incomplete Frontmatter',
4571
+ description: 'Skill frontmatter is missing required fields',
4572
+ category: 'skill',
4573
+ severity: 'high',
4574
+ passed: false,
4575
+ message: `${relativePath}: Missing required frontmatter fields: ${missingFields.join(', ')}. These are needed for capability declaration and version tracking.`,
4576
+ file: relativePath,
4577
+ fixable: true,
4578
+ fix: `Add missing fields to frontmatter: ${missingFields.join(', ')}`,
4579
+ guidance: 'Incomplete frontmatter prevents proper capability validation. Every skill should declare name, version, and capabilities.',
4580
+ });
4581
+ }
4582
+ else {
4583
+ findings.push({
4584
+ checkId: 'SKILL-020',
4585
+ name: 'Valid Frontmatter',
4586
+ description: 'Skill file has valid YAML frontmatter with required fields',
4587
+ category: 'skill',
4588
+ severity: 'high',
4589
+ passed: true,
4590
+ message: 'Skill has valid frontmatter with name, version, and capabilities',
4591
+ file: relativePath,
4592
+ fixable: false,
4593
+ fix: 'No action needed',
4594
+ guidance: 'Skill frontmatter is properly configured.',
4595
+ });
4596
+ }
4597
+ }
4598
+ // SKILL-021: Overprivileged permissions (dangerous capability combinations)
4599
+ const dangerousCombos = [
4600
+ {
4601
+ combo: ['filesystem:*', 'network:outbound'],
4602
+ reason: 'filesystem:* + network:outbound enables data exfiltration',
4603
+ },
4604
+ {
4605
+ combo: ['credential:read', 'network:outbound'],
4606
+ reason: 'credential:read + network:outbound enables credential exfiltration',
4607
+ },
4608
+ ];
4609
+ const capPatterns = content.match(/(?:filesystem|network|credential|tool):[a-z*]+/g) || [];
4610
+ const allCaps = [...new Set(capPatterns)];
4611
+ // Also include capabilities from frontmatter if parsed
4612
+ const declaredCapsForPriv = (0, skill_capability_validator_1.parseDeclaredCapabilities)(content);
4613
+ for (const dc of declaredCapsForPriv.capabilities) {
4614
+ if (!allCaps.includes(dc))
4615
+ allCaps.push(dc);
4616
+ }
4617
+ for (const { combo, reason } of dangerousCombos) {
4618
+ const matchCap = (actual, pattern) => {
4619
+ if (actual === pattern)
4620
+ return true;
4621
+ if (pattern.endsWith(':*'))
4622
+ return actual.startsWith(pattern.slice(0, -1));
4623
+ if (actual.endsWith(':*'))
4624
+ return pattern.startsWith(actual.slice(0, -1));
4625
+ return false;
4626
+ };
4627
+ const hasFirst = allCaps.some(c => matchCap(c, combo[0]));
4628
+ const hasSecond = allCaps.some(c => matchCap(c, combo[1]));
4629
+ if (hasFirst && hasSecond) {
4630
+ findings.push({
4631
+ checkId: 'SKILL-021',
4632
+ name: 'Overprivileged Permissions',
4633
+ description: 'Skill has dangerous capability combination that enables exfiltration',
4634
+ category: 'skill',
4635
+ severity: 'high',
4636
+ passed: false,
4637
+ message: `${relativePath}: ${reason}. Restrict filesystem access to specific paths or remove outbound network access.`,
4638
+ file: relativePath,
4639
+ fixable: false,
4640
+ fix: 'Restrict capabilities to minimum required permissions',
4641
+ guidance: 'Dangerous capability combinations can enable data or credential exfiltration. Follow the principle of least privilege.',
4642
+ });
4643
+ }
4644
+ }
4645
+ // SKILL-022: Environment variable exfiltration
4646
+ const envAccessPatterns = [
4647
+ /process\.env/,
4648
+ /os\.environ/,
4649
+ /\$ENV\{/,
4650
+ /System\.getenv/,
4651
+ /printenv/,
4652
+ /\$\(env\)/,
4653
+ /\$\(printenv/,
4654
+ /\$HOME\b/,
4655
+ /\$\{[A-Z_]+\}/,
4656
+ ];
4657
+ const outboundPatterns = [
4658
+ /network:outbound/,
4659
+ /fetch\s*\(/,
4660
+ /https?:\/\//,
4661
+ /XMLHttpRequest/,
4662
+ /\.send\s*\(/,
4663
+ /curl\s/,
4664
+ /wget\s/,
4665
+ ];
4666
+ const hasEnvAccess = envAccessPatterns.some(p => p.test(content));
4667
+ const hasOutbound = outboundPatterns.some(p => p.test(content));
4668
+ if (hasEnvAccess && hasOutbound) {
4669
+ findings.push({
4670
+ checkId: 'SKILL-022',
4671
+ name: 'Environment Variable Exfiltration Risk',
4672
+ description: 'Skill accesses environment variables and has outbound network capability',
4673
+ category: 'skill',
4674
+ severity: 'critical',
4675
+ passed: false,
4676
+ message: `${relativePath}: Skill accesses environment variables AND has outbound network capability. This combination can exfiltrate secrets via network requests.`,
4677
+ file: relativePath,
4678
+ fixable: false,
4679
+ fix: 'Remove outbound network access or environment variable reads',
4680
+ guidance: 'Skills that read environment variables and send data externally can exfiltrate API keys, tokens, and other secrets stored in environment variables.',
4681
+ });
4682
+ }
4683
+ // SKILL-023: Obfuscated code patterns
4684
+ const obfuscationPatterns = [
4685
+ { pattern: /atob\s*\(/, label: 'atob() base64 decode' },
4686
+ { pattern: /Buffer\.from\s*\(/, label: 'Buffer.from() decode' },
4687
+ { pattern: /eval\s*\(/, label: 'eval() dynamic execution' },
4688
+ { pattern: /String\.fromCharCode/, label: 'String.fromCharCode obfuscation' },
4689
+ { pattern: /\\x[0-9a-fA-F]{2}/, label: 'hex-encoded string' },
4690
+ { pattern: /(?:atob|Buffer\.from)\s*\([^)]+\)[\s\S]*?eval\s*\(/, label: 'base64+eval combo' },
4691
+ { pattern: /base64\s+-d/, label: 'shell base64 decode' },
4692
+ { pattern: /eval\s+\$\(/, label: 'shell eval $(...)' },
4693
+ { pattern: /\becho\s+['"][A-Za-z0-9+/=]{20,}['"]\s*\|\s*base64/, label: 'echo+base64 pipe' },
4694
+ { pattern: /new\s+Function\s*\(/, label: 'new Function() dynamic execution' },
4695
+ ];
4696
+ for (const { pattern, label } of obfuscationPatterns) {
4697
+ if (pattern.test(content)) {
4698
+ findings.push({
4699
+ checkId: 'SKILL-023',
4700
+ name: 'Obfuscated Code Pattern',
4701
+ description: 'Skill contains obfuscated code that may hide malicious behavior',
4702
+ category: 'skill',
4703
+ severity: 'high',
4704
+ passed: false,
4705
+ message: `${relativePath}: Detected ${label}. Obfuscated code in skills can hide malicious behavior and should be reviewed.`,
4706
+ file: relativePath,
4707
+ fixable: false,
4708
+ fix: 'Replace obfuscated code with readable equivalent',
4709
+ guidance: 'Obfuscated code (base64 decode, eval, hex-encoded strings) in skills is a strong indicator of hidden malicious behavior. Review and replace with transparent code.',
4710
+ });
4711
+ break; // One finding per file for obfuscation
4712
+ }
4713
+ }
4714
+ // SKILL-024: Unbounded tool chaining
4715
+ const hasToolChain = allCaps.some(c => c.includes('tool:chain'));
4716
+ if (hasToolChain) {
4717
+ const hasFm = !!fmMatch;
4718
+ const fmContent = hasFm ? fmMatch[1] : '';
4719
+ const hasMaxIterations = hasFm && (/maxIterations/i.test(fmContent) ||
4720
+ /iterationLimit/i.test(fmContent));
4721
+ if (!hasMaxIterations) {
4722
+ findings.push({
4723
+ checkId: 'SKILL-024',
4724
+ name: 'Unbounded Tool Chaining',
4725
+ description: 'Skill declares tool:chain capability without iteration limits',
4726
+ category: 'skill',
4727
+ severity: 'medium',
4728
+ passed: false,
4729
+ message: `${relativePath}: Skill declares tool:chain capability without maxIterations or iterationLimit. Unbounded chaining can lead to infinite loops or resource exhaustion.`,
4730
+ file: relativePath,
4731
+ fixable: true,
4732
+ fix: 'Add maxIterations or iterationLimit to skill frontmatter',
4733
+ guidance: 'Tool chaining without iteration limits can cause infinite loops, resource exhaustion, or runaway costs. Set a reasonable maxIterations value in frontmatter.',
4734
+ });
4735
+ }
4736
+ }
4546
4737
  }
4547
4738
  return findings;
4548
4739
  }