TypeScript API
The CLI is the fastest way to get started. The TypeScript API is for when you need Firmis inside your own tooling — custom CI scripts, security dashboards, editor integrations, or automated remediation pipelines.
Installation
Section titled “Installation”Install firmis-scanner as a dependency rather than running it via npx.
npm install firmis-scanner# or with pnpm / yarnpnpm add firmis-scanneryarn add firmis-scannerBasic scan
Section titled “Basic scan”import { ScanEngine } from 'firmis-scanner'import type { FirmisConfig, ScanResult } from 'firmis-scanner'
const config: FirmisConfig = { severity: 'low', output: 'json', verbose: false, concurrency: 4,}
const engine = new ScanEngine(config)await engine.initialize()
const result: ScanResult = await engine.scan()
console.log(`Security grade: ${result.score}`)console.log(`Threats found: ${result.summary.threatsFound}`)console.log(`Critical: ${result.summary.bySeverity.critical}`)console.log(`High: ${result.summary.bySeverity.high}`)Scan a specific path
Section titled “Scan a specific path”import { ScanEngine } from 'firmis-scanner'import type { FirmisConfig } from 'firmis-scanner'
async function scanDirectory(targetPath: string): Promise<void> { const config: FirmisConfig = { targetPath, severity: 'high', output: 'json', verbose: false, concurrency: 4, failOnSeverity: 'high', }
const engine = new ScanEngine(config) await engine.initialize()
const result = await engine.scan()
if (result.summary.bySeverity.critical > 0 || result.summary.bySeverity.high > 0) { console.error(`Found ${result.summary.threatsFound} threats — grade ${result.score}`) process.exit(1) }
console.log(`Clean scan — grade ${result.score}`)}
await scanDirectory('./packages/agent')Scan specific platforms
Section titled “Scan specific platforms”import { ScanEngine } from 'firmis-scanner'import type { FirmisConfig, PlatformType } from 'firmis-scanner'
const platforms: PlatformType[] = ['mcp', 'claude']
const config: FirmisConfig = { platforms, severity: 'low', output: 'json', verbose: false, concurrency: 4,}
const engine = new ScanEngine(config)await engine.initialize()
const result = await engine.scan()
for (const platformResult of result.platforms) { console.log(`Platform: ${platformResult.platform}`) console.log(` Threats: ${platformResult.threats.length}`)
for (const threat of platformResult.threats) { console.log(` [${threat.severity.toUpperCase()}] ${threat.ruleId}: ${threat.message}`) console.log(` at ${threat.location.file}:${threat.location.line}`) }}Progress events
Section titled “Progress events”Use onProgress to stream scan milestones to your UI or logs.
import { ScanEngine } from 'firmis-scanner'import type { FirmisConfig, ProgressEvent } from 'firmis-scanner'
const config: FirmisConfig = { severity: 'low', output: 'json', verbose: false, concurrency: 4, onProgress: (event: ProgressEvent) => { switch (event.type) { case 'rules_loaded': console.log('[1/3] Rules loaded') break case 'discovery_complete': console.log('[2/3] Discovery complete') break case 'platform_start': console.log(`[3/3] Scanning ${event.platform}...`) break case 'component_complete': console.log(` Done: ${event.component}`) break } },}
const engine = new ScanEngine(config)await engine.initialize()const result = await engine.scan()
console.log(`Finished — ${result.summary.threatsFound} threats in ${result.duration}ms`)SARIF output
Section titled “SARIF output”GitHub, VS Code, and every major SAST dashboard speaks SARIF. Generate it directly from the API:
import { ScanEngine, getReporter } from 'firmis-scanner'import type { FirmisConfig } from 'firmis-scanner'import { writeFile } from 'node:fs/promises'
const config: FirmisConfig = { severity: 'low', output: 'sarif', outputFile: 'firmis.sarif', verbose: false, concurrency: 4,}
const engine = new ScanEngine(config)await engine.initialize()
const result = await engine.scan()
const reporter = getReporter('sarif')const sarifOutput = await reporter.generate(result, config)
await writeFile('firmis.sarif', sarifOutput)console.log('SARIF written to firmis.sarif')Custom rules
Section titled “Custom rules”Load additional YAML rule files alongside the 209 built-in rules.
import { ScanEngine } from 'firmis-scanner'import type { FirmisConfig } from 'firmis-scanner'import { resolve } from 'node:path'
const config: FirmisConfig = { severity: 'low', output: 'json', verbose: false, concurrency: 4, customRules: [ resolve('./rules/company-policy.yaml'), resolve('./rules/internal-secrets.yaml'), ],}
const engine = new ScanEngine(config)await engine.initialize()
const result = await engine.scan()console.log(`Scanned with ${result.platforms.length} platforms`)Ignore rules programmatically
Section titled “Ignore rules programmatically”import { ScanEngine } from 'firmis-scanner'import type { FirmisConfig } from 'firmis-scanner'
const config: FirmisConfig = { severity: 'low', output: 'json', verbose: false, concurrency: 4, ignoreRules: ['sd-015', 'pi-003'], // skip these rule IDs exclude: ['test/fixtures/', 'dist/'], // skip these paths}
const engine = new ScanEngine(config)await engine.initialize()
const result = await engine.scan()Error handling
Section titled “Error handling”import { ScanEngine, FirmisError, isFirmisError } from 'firmis-scanner'import type { FirmisConfig, ScanResult } from 'firmis-scanner'
async function runScan(targetPath: string): Promise<ScanResult | null> { const config: FirmisConfig = { targetPath, severity: 'low', output: 'json', verbose: false, concurrency: 4, }
try { const engine = new ScanEngine(config) await engine.initialize() return await engine.scan() } catch (err) { if (isFirmisError(err)) { // Structured Firmis error with a user-friendly message console.error(`Firmis error [${err.code}]: ${err.message}`) } else { console.error('Unexpected error during scan:', err) } return null }}
const result = await runScan('./agent')if (result) { console.log(`Grade: ${result.score}`)}API reference
Section titled “API reference”ScanEngine
Section titled “ScanEngine”The main entry point for programmatic scanning.
class ScanEngine { constructor(config: FirmisConfig) initialize(): Promise<void> scan(): Promise<ScanResult>}FirmisConfig
Section titled “FirmisConfig”Configuration object passed to ScanEngine.
interface FirmisConfig { /** Platforms to scan (undefined = auto-detect all) */ platforms?: PlatformType[] /** Target path to scan */ targetPath?: string /** Minimum severity to report */ severity: SeverityLevel /** Custom rule file paths */ customRules?: string[] /** Paths to exclude */ exclude?: string[] /** Output format */ output: OutputFormat /** Output file path (for json/sarif/html) */ outputFile?: string /** Enable verbose logging */ verbose: boolean /** Parallel worker count */ concurrency: number /** Stop on first critical threat */ failFast?: boolean /** Suppress terminal output */ quiet?: boolean /** Rule IDs to skip */ ignoreRules?: string[] /** Severity that triggers non-zero exit */ failOnSeverity?: SeverityLevel /** Progress callback */ onProgress?: (event: ProgressEvent) => void}ScanResult
Section titled “ScanResult”Returned by engine.scan().
interface ScanResult { id: string startedAt: Date completedAt: Date duration: number // milliseconds platforms: PlatformScanResult[] summary: ScanSummary score: SecurityGrade // 'A' | 'B' | 'C' | 'D' | 'F' runtimeRisksNotCovered: string[]}Threat
Section titled “Threat”Individual detected threat.
interface Threat { id: string ruleId: string category: ThreatCategory severity: SeverityLevel // 'low' | 'medium' | 'high' | 'critical' message: string evidence: Evidence[] location: SourceLocation confidence: number // 0–1 confidenceTier: ConfidenceTier // 'suspicious' | 'likely' | 'confirmed' remediation?: string}getReporter
Section titled “getReporter”Get a reporter instance for a given output format.
import { getReporter } from 'firmis-scanner'
const reporter = getReporter('sarif') // 'terminal' | 'json' | 'sarif' | 'html'const output = await reporter.generate(result, config)Error classes
Section titled “Error classes”import { FirmisError, ConfigurationError, PlatformError, isFirmisError } from 'firmis-scanner'
// isFirmisError narrows unknown to FirmisErrorif (isFirmisError(err)) { console.error(err.code, err.message)}TypeScript configuration
Section titled “TypeScript configuration”Firmis is written in TypeScript and ships its own types. No @types/firmis-scanner package is needed.
{ "compilerOptions": { "module": "ESNext", "moduleResolution": "bundler", "target": "ES2022", "strict": true }}What to do next
Section titled “What to do next”- GitHub Actions integration → — run this API in CI with a security gate
- GitLab CI integration → — same for GitLab pipelines
- Configuration reference → — every
FirmisConfigfield documented - SARIF output reference → — what the SARIF reporter produces
- Threat categories reference → — all 209 rules across 16 categories