feat: cloud / mobile / binary-RE offensive loadouts (arsenal + detection benchmarks)

Three new category loadouts, each = arsenal + an offline-scorable benchmark, opt-in and
honestly scoped (roster marks all 🚧; pacu/frida stay catalog_only, unit-tested non-mintable):
- cloud: aws/az/gcloud + scoutsuite/cloudfox/pmapper; cloud:bench IaC-misconfig detection
- mobile: apkleaks/mobsfscan/drozer/objection/class-dump; mobile:bench static ruleset
- binary/RE: ghidra/objdump/checksec/strings/readelf/gdb; binary:bench decompiled-sink detector

Mobile/binary benches are DETECTOR REGRESSION CHECKS (rules+fixtures co-authored — a pass is a
floor, not a detection rate). No new headline numbers; verify-claims 24/24.

tsc clean; 337 tests; lint 0 errors.
This commit is contained in:
t3mp3st contributors committed 2026-07-05 21:49:19 -04:00
1 parent a5667374bf
commit 537d5345dc
17 files changed
+1026

No files matched your search

+26
View File
@@ -56,6 +56,8 @@ describe('adapterToCustomTool — mint gate', () => {
expect(adapterToCustomTool(adapter('metasploit'), deps)).toBeNull(); // catalog_only
expect(adapterToCustomTool(adapter('hydra'), deps)).toBeNull(); // catalog_only
expect(adapterToCustomTool(adapter('bloodhound'), deps)).toBeNull(); // import_only
expect(adapterToCustomTool(adapter('pacu'), deps)).toBeNull(); // catalog_only (AWS exploitation framework)
expect(adapterToCustomTool(adapter('frida'), deps)).toBeNull(); // catalog_only (runtime code injection)
});
it('mints a command-ready adapter with a working name + category', () => {
@@ -67,6 +69,30 @@ describe('adapterToCustomTool — mint gate', () => {
});
});
describe('cloud/mobile category loadouts — presence + risk gating', () => {
const deps = makeDeps();
const execOf = (id: string) => adapter(id).execution;
it('cloud: assessment/recon tools are receipt-gated; the exploitation framework is catalog-only', () => {
expect(execOf('scoutsuite')).toBe('receipt_required');
expect(execOf('cloudfox')).toBe('receipt_required');
expect(execOf('pmapper')).toBe('receipt_required');
expect(execOf('pacu')).toBe('catalog_only');
expect(adapter('scoutsuite').category).toBe('cloud');
expect(adapterToCustomTool(adapter('pacu'), deps)).toBeNull(); // never callable
});
it('mobile: static scanner is safe; dynamic/runtime tools are gated', () => {
expect(execOf('apkleaks')).toBe('safe_command');
expect(execOf('mobsfscan')).toBe('safe_command'); // static source scanner — safe
expect(execOf('objection')).toBe('receipt_required');
expect(execOf('frida')).toBe('catalog_only');
expect(adapter('apkleaks').category).toBe('mobile');
expect(adapterToCustomTool(adapter('frida'), deps)).toBeNull(); // never callable
expect(adapterToCustomTool(adapter('apkleaks'), deps)).not.toBeNull(); // safe, mintable
});
});
describe('adapterToCustomTool — degrade / scope / execution', () => {
it('a missing binary DEGRADES (does not throw) and surfaces the installHint', async () => {
const deps = makeDeps({ isToolAvailable: async () => false });
+40
View File
@@ -0,0 +1,40 @@
/**
* Locks the Binary/RE loadout's decompiled-vuln detector + benchmark into CI: the ruleset must catch
* every seeded memory-safety / injection sink in the corpus and stay clean on the bounded-function
* controls (strncpy/snprintf/fgets, literal printf). Deterministic + self-contained.
*/
import { describe, it, expect } from 'vitest';
import { loadCorpus, detect, scoreCorpus, RULES } from '../../scripts/binary-vuln-bench.mjs';
describe('binary/RE decompiled-vuln benchmark', () => {
const corpus = loadCorpus();
const score = scoreCorpus(corpus);
it('corpus has seeded sinks + safe controls with ground truth', () => {
expect(corpus.fixtures.length).toBeGreaterThanOrEqual(6);
expect(corpus.fixtures.some((f) => f.clean)).toBe(true);
expect(corpus.fixtures.every((f) => f.clean || f.expect.length > 0)).toBe(true);
});
it('detects every seeded sink', () => {
expect(score.detection.hits).toBe(score.detection.total);
expect(score.detection.total).toBeGreaterThanOrEqual(6);
});
it('does not false-positive on bounded-function controls', () => {
expect(score.discrimination.falsePositives).toBe(0);
// the bounded equivalents must NOT trip the unbounded rules
expect(detect('strncpy(buf, src, sizeof(buf) - 1);').size).toBe(0);
expect(detect('snprintf(out, sizeof(out), "%s", x);').size).toBe(0);
expect(detect('fgets(name, sizeof(name), stdin);').size).toBe(0);
// ...but the unbounded ones must
expect(detect('strcpy(buf, src);').has('B-STRCPY')).toBe(true);
expect(detect('printf(user);').has('B-FORMAT-STRING')).toBe(true);
});
it('ruleset spans memory-safety + injection + integer-overflow', () => {
expect(RULES.length).toBeGreaterThanOrEqual(6);
expect(RULES.some((r) => r.id === 'B-CMD-INJECTION')).toBe(true);
expect(RULES.some((r) => r.id === 'B-INT-OVERFLOW')).toBe(true);
});
});
@@ -0,0 +1,37 @@
/**
* Locks the Cloud loadout's IaC-misconfig benchmark scorer into CI: the parser must map checkov
* findings to fixtures, every seeded misconfig must be detected, and the locked-down control must
* produce zero misconfig false-positives — validated against the committed sample (no checkov needed).
*/
import { describe, it, expect } from 'vitest';
import { readFileSync } from 'fs';
import { join } from 'path';
// The benchmark logic is authored in the bench script (ESM .mjs); import its pure functions directly.
import { loadCorpus, parseCheckov, scoreCorpus } from '../../scripts/cloud-misconfig-bench.mjs';
const sample = JSON.parse(readFileSync(join(process.cwd(), 'bench/cloud-misconfig/sample-checkov-output.json'), 'utf8'));
describe('cloud IaC-misconfig benchmark scorer', () => {
const corpus = loadCorpus();
const score = scoreCorpus(corpus, parseCheckov(sample));
it('the corpus has misconfigured fixtures + a clean control, all with ground truth', () => {
expect(corpus.fixtures.length).toBeGreaterThanOrEqual(5);
expect(corpus.fixtures.some((f: { clean?: boolean }) => f.clean)).toBe(true);
expect((corpus.target_checks || []).length).toBeGreaterThan(0);
});
it('detects every seeded misconfiguration on the committed sample', () => {
expect(score.detection.hits).toBe(score.detection.total);
expect(score.detection.total).toBeGreaterThanOrEqual(5);
});
it('does not false-positive on the locked-down control', () => {
expect(score.discrimination.falsePositives).toBe(0);
});
it('parser is tolerant of checkov multi-framework (array) output', () => {
const asArray = parseCheckov([sample]);
expect(Object.keys(asArray).length).toBe(Object.keys(parseCheckov(sample)).length);
});
});
+35
View File
@@ -0,0 +1,35 @@
/**
* Locks the Mobile loadout's static-analysis detector + benchmark into CI: the built-in ruleset must
* catch every seeded manifest-misconfig / hardcoded-secret in the corpus and stay clean on the hardened
* controls. Deterministic + self-contained (no external tool).
*/
import { describe, it, expect } from 'vitest';
import { loadCorpus, detect, scoreCorpus, RULES } from '../../scripts/mobile-static-bench.mjs';
describe('mobile static-analysis benchmark', () => {
const corpus = loadCorpus();
const score = scoreCorpus(corpus);
it('corpus has seeded fixtures + hardened controls with ground truth', () => {
expect(corpus.fixtures.length).toBeGreaterThanOrEqual(6);
expect(corpus.fixtures.some((f) => f.clean)).toBe(true);
expect(corpus.fixtures.every((f) => f.clean || f.expect.length > 0)).toBe(true);
});
it('detects every seeded manifest/secret issue', () => {
expect(score.detection.hits).toBe(score.detection.total);
expect(score.detection.total).toBeGreaterThanOrEqual(6);
});
it('does not false-positive on hardened controls', () => {
expect(score.discrimination.falsePositives).toBe(0);
});
it('ruleset spans manifest misconfig + hardcoded secrets, kind-scoped', () => {
expect(detect('android:debuggable="true"', 'manifest').has('M-DEBUGGABLE')).toBe(true);
expect(detect('String k = "AKIAIOSFODNN7EXAMPLE";', 'code').has('S-AWS-KEY')).toBe(true);
// a manifest xmlns http:// must NOT trip the code-only cleartext-endpoint rule
expect(detect('xmlns:android="http://schemas.android.com/apk/res/android"', 'manifest').has('E-HTTP-URL')).toBe(false);
expect(RULES.length).toBeGreaterThanOrEqual(6);
});
});