npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@haldir/scanner

v0.1.1

Published

Haldir scanner — static analysis for agent skill threat detection (Layer 1)

Downloads

183

Readme

@haldir/scanner

Static analysis engine for agent skills. Detects malicious patterns using 70 regex-based rules across 7 threat categories.

What This Package Does

Scans agent skill directories for security threats:

  • Exfiltration - Environment harvesting, data leaks, context exposure
  • Privilege escalation - sudo, credential access, allowed tool abuse
  • Supply chain - Unpinned deps, curl|sh, obfuscated execution
  • Prompt injection - Instruction override, hidden directives
  • Persistence - Reverse shells, memory poisoning, cron jobs
  • Campaign indicators - Paste services, URL shorteners, C2 channels
  • Credential exposure - API keys, tokens, private keys in code

Installation

npm install @haldir/scanner

Quick Start

import { scanDirectory } from '@haldir/scanner';

const result = await scanDirectory('./my-skill');

console.log(`Status: ${result.status}`); // 'pass' | 'flag' | 'reject'
console.log(`Files scanned: ${result.files_scanned}`);
console.log(`Findings: ${result.findings.length}`);
console.log(`Summary: ${JSON.stringify(result.summary)}`);

// Check findings
for (const finding of result.findings) {
  console.log(`[${finding.severity}] ${finding.file}:${finding.line}`);
  console.log(`  ${finding.message}`);
  console.log(`  Match: ${finding.match}`);
}

CLI Usage

# Scan a skill directory
haldir scan ./my-skill

# JSON output
haldir scan ./my-skill --json

# Only report high/critical
haldir scan ./my-skill --severity high

# Fail on any finding
haldir scan ./my-skill --strict

API

scanDirectory(dirPath, config?)

Scans a directory for security threats.

Parameters:

interface ScanConfig {
  // Maximum files to scan (default: 10,000)
  maxFiles?: number;

  // Maximum file size (default: 10MB)
  maxFileSize?: number;

  // Directories to skip (default: ['.vault', 'node_modules', '.git', '__pycache__'])
  skipDirs?: string[];

  // Minimum severity to report (default: 'low')
  severityThreshold?: 'low' | 'medium' | 'high' | 'critical';

  // Custom patterns (default: PATTERN_DB)
  patterns?: ThreatPattern[];

  // Stop on first critical finding (default: false)
  stopOnFirstCritical?: boolean;
}

Returns: Promise<ScanResult>

interface ScanResult {
  // Overall assessment
  status: 'pass' | 'flag' | 'reject';

  // Performance metrics
  duration_ms: number;
  files_scanned: number;
  files_skipped: number;
  patterns_checked: number;

  // Findings
  findings: Finding[];
  summary: {
    critical: number;
    high: number;
    medium: number;
    low: number;
  };
}

interface Finding {
  pattern_id: string;          // e.g. 'env_harvest_node'
  category: ThreatCategory;    // e.g. 'exfiltration'
  severity: Severity;          // 'critical' | 'high' | 'medium' | 'low'
  file: string;                // relative path from skill root
  line: number;                // 1-indexed
  column: number;              // 0-indexed
  match: string;               // matched substring
  context: string;             // full line
  message: string;             // human-readable description
}

Status Mapping

| Severity | Status | |----------|--------| | Any critical | reject | | Any high/medium | flag | | Only low | pass | | No findings | pass |

Threat Categories

1. Exfiltration (E1-E4)

Detects data extraction attempts:

| Pattern | Example | |---------|---------| | env_harvest_node | process.env | | env_harvest_python | os.environ | | fs_enumerate_* | fs.readdir, os.listdir | | data_exfil_* | HTTP POST with large payloads | | context_leakage | Agent context in network requests |

Severity: High

2. Privilege Escalation (PE1-PE3)

Detects privilege abuse:

| Pattern | Example | |---------|---------| | sudo_escalation | sudo, doas | | credential_access | /etc/passwd, ~/.ssh/ | | allowed_tool_abuse | Misuse of declared capabilities | | docker_socket_access | /var/run/docker.sock | | proc_access | /proc/*/environ |

Severity: High

3. Supply Chain (SC1-SC3)

Detects dependency attacks:

| Pattern | Example | |---------|---------| | unpinned_deps | ^1.0.0, >=2.0.0 | | curl_pipe_sh | curl ... \| sh | | obfuscated_exec | eval(atob(...)) | | base64_pipe | Base64-encoded commands | | git_deps | "dep": "git://..." |

Severity: Critical (curl|sh), High (others)

4. Prompt Injection (P1-P4)

Detects prompt manipulation:

| Pattern | Example | |---------|---------| | instruction_override | "Ignore previous instructions" | | hidden_unicode | Zero-width characters | | persona_override | "You are now..." | | exfil_command | Hidden commands in prompts |

Severity: High/Critical

5. Persistence

Detects persistent backdoors:

| Pattern | Example | |---------|---------| | reverse_shell | nc -e /bin/bash | | memory_poison | Mnemo corruption attempts | | cron_install | crontab -e | | startup_script | ~/.bashrc modification |

Severity: Critical

6. Campaign Indicators

Detects organized attacks:

| Pattern | Example | |---------|---------| | paste_service | pastebin.com, hastebin | | url_shortener | bit.ly, tinyurl | | c2_channel | Known C2 domains |

Severity: Medium

7. Credential Exposure

Detects hardcoded secrets:

| Pattern | Example | |---------|---------| | aws_key | AKIA[A-Z0-9]{16} | | openai_key | sk-[A-Za-z0-9]{48} | | stripe_key | sk_live_[A-Za-z0-9]+ | | github_token | ghp_[A-Za-z0-9]+ | | private_key_marker | -----BEGIN PRIVATE KEY----- | | jwt_token | eyJ[A-Za-z0-9-_]+\.eyJ... |

Severity: High/Critical

Performance

  • Speed: ~1s for typical skill (10-20 files)
  • Scalability: 10,000 files in <30s
  • Pattern count: 70 pre-compiled regex patterns
  • Extension filtering: Patterns only run on relevant file types

Limitations

What Scanner Catches

✅ Known malicious patterns (environment harvesting, reverse shells) ✅ Credential exposure (API keys, tokens) ✅ Suspicious commands (curl|sh, sudo, base64) ✅ Supply chain issues (unpinned deps, git dependencies)

What Scanner Misses

Complex dataflow obfuscation — Multi-step variable chains across many lines may evade both line-by-line and multiline regex. Full AST/dataflow analysis is not implemented. ❌ Semantic malice — Requires LLM audit (see @haldir/reviewer) ❌ Runtime behavior — Sandbox testing needed (see @haldir/sandbox) ❌ Zero-day patterns — Not in pattern database until added ❌ Novel typosquats — Levenshtein detection covers ~90 popular packages; less popular package typosquats may not be caught ❌ External registry validation — Cannot verify whether a dependency name exists on npm

Recommendation: Use scanner as Layer 1 of a multi-layer pipeline. Combine with:

See @haldir/pipeline for full vetting orchestration.

Custom Patterns

import { scanDirectory, type ThreatPattern } from '@haldir/scanner';

const customPatterns: ThreatPattern[] = [
  {
    id: 'my_custom_check',
    category: 'exfiltration',
    severity: 'high',
    name: 'Custom data exfiltration check',
    regex: /sendDataToAttacker\(/,
    fileExtensions: ['.js', '.ts'],
    description: 'Detects calls to sendDataToAttacker function'
  }
];

const result = await scanDirectory('./skill', {
  patterns: customPatterns
});

Exit Codes (CLI)

| Code | Meaning | |------|---------| | 0 | Scan passed (no findings or only low severity) | | 1 | Findings detected (reject or flag + --strict) | | 2 | Error (invalid arguments, filesystem error) |

See Also

License

Apache 2.0