uluka
v2.1.0
Published
Security Evaluation CLI - Verify that code does what it claims to do
Maintainers
Readme
Uluka
Security Claim Verification CLI — Verify that code does what it claims to do
Uluka is the only tool that verifies security claims match reality. It extracts security claims from code comments, README files, JSDoc, and function signatures, then verifies them against the actual implementation. It complements tools like Semgrep and CodeQL by importing their findings and adding claim verification on top.
Features
- Claim Detection — Discovers security claims in code comments, README, JSDoc, and function names across JavaScript, TypeScript, Python, Java, and more
- Automatic Claim Discovery — Infers security claims from function names (
authenticate*,encrypt*) and middleware patterns (helmet,cors,rateLimit) - Evidence-Based Verification — Validates claims using deterministic rule-based verifiers including encryption, authentication, input validation, and more
- SARIF Import — Import findings from Semgrep, CodeQL, Gitleaks, and any SARIF 2.1.0 tool via
--importor stdin piping - Multi-Tool Correlation — Cross-references findings across tools with confidence boosting when multiple tools agree
- LLM-Powered Analysis — Optional Claude integration for nuanced claim reasoning and catch-all verification
- Multi-Language Support — Works with JavaScript, TypeScript, Python, Java, Go, Ruby, PHP, C#, and more
- Flexible Output — Terminal-friendly text with source badges, JSON with source breakdowns, or multi-run SARIF for security dashboards
- Configurable Rules — Customize severity levels, disable specific rules, configure trusted sources, and control verification behavior via
.ulukarc - Input Validation Verifier — Detects Zod, Joi, express-validator, and class-validator with route-to-validation linking
- Cross-File Data Flow Analysis — Traces tainted user input (req.body, req.params, req.query) across module boundaries to security-sensitive sinks (db.query, eval, exec) with sanitizer-aware filtering
- Incremental Analysis — Content-hash caching via
--incrementalflag for fast re-analysis of large codebases; only changed files are re-analyzed - Claude Code Hooks — Native hook integration for post-tool-use scanning and pre-commit blocking via
uluka hook scananduluka hook pre-commit
Installation
Global Installation (recommended for CLI usage)
npm install -g ulukaLocal Installation (for project-specific usage)
npm install --save-dev ulukaRequirements
- Node.js >= 18.0.0
Quick Start
Scan for Security Claims
uluka scan src/Discovers all security-related claims in your codebase:
Found 5 security claims:
- Encryption: "uses AES-256-GCM encryption" (src/auth/encrypt.ts:12)
- Authentication: "requires JWT with RS256" (src/api/middleware.ts:34)
- Authorization: "admin-only endpoint" (src/api/admin.ts:8)Verify Claims Against Implementation
uluka verify src/Validates each claim and shows verification status:
Verification Results:
✓ Encryption claim verified (found crypto.createCipheriv with aes-256-gcm)
✓ Authentication claim verified (JWT signature validation with RS256 present)
✗ Authorization claim FAILED (no role check found before handler execution)Ecosystem Integration
Uluka works best alongside existing security tools, not as a replacement.
Import Semgrep Results
semgrep --sarif -o semgrep.sarif .
uluka verify src/ --import semgrep.sarifImport CodeQL Results
codeql database analyze db --format=sarif-latest --output=codeql.sarif
uluka verify src/ --import codeql.sarifPipe via stdin
semgrep --sarif . | uluka verify src/ --import -Import Multiple Tools
uluka verify src/ --import semgrep.sarif --import codeql.sarifImport-Only Mode
uluka verify --import-only --import semgrep.sarif --format sarifImported findings appear with source badges in terminal output (e.g., [semgrep], [codeql]) and as separate runs in SARIF output.
Data Flow Analysis
Uluka traces tainted user input through your codebase to detect unsanitized data reaching security-sensitive sinks.
What It Detects
- Taint sources:
req.body,req.params,req.query,req.headers - Sinks:
db.query,eval,exec,fs.readFile,res.send,innerHTML - Cross-file flows: Tainted data passed to imported functions that contain sinks
- Sanitizer awareness: Flows through Zod
.parse(), Joi.validate(), express-validator are not flagged
Example
uluka verify src/✗ Unsanitized data flow detected
Source: req.body.id (handler.ts:5)
Sink: db.query (database.ts:12)
Severity: high
Remediation: Validate or sanitize user input before passing to db.queryIncremental Analysis
For large codebases, use the --incremental flag to cache results and only re-analyze changed files.
# First run: full analysis, results cached
uluka verify src/ --incremental
# Second run: only changed files re-analyzed
uluka verify src/ --incremental
# Clear the cache
uluka cache clear
# Check cache status
uluka cache statusCache uses SHA-256 content hashing (not timestamps) and validates integrity on read. Configure the cache directory in .ulukarc:
cache:
directory: .uluka-cache # defaultClaude Code Hooks
Uluka integrates with Claude Code as a post-tool-use hook or pre-commit hook.
Post-Tool-Use Hook
Add to .claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "uluka hook scan --files $FILEPATH"
}
]
}
]
}
}Pre-Commit Hook
uluka hook pre-commitBlocks commits with critical security findings. Use --force to report findings without blocking.
Output Format
Hook output is structured JSON (no ANSI colors) for machine consumption:
{
"summary": {
"filesScanned": 3,
"findingsCount": 1,
"criticalCount": 0,
"highCount": 1
},
"findings": [...],
"warnings": []
}See docs/hooks.md for complete setup guide.
Configure Trusted Sources
In .ulukarc:
integrations:
trustedSources:
- semgrep
- codeql
- gitleaks
imports:
- path: ./reports/semgrep.sarif
tool: semgrepConfidence Filtering
Filter findings by confidence level:
uluka verify src/ --min-confidence highWhen multiple tools report the same finding, confidence is automatically boosted.
CLI Commands
uluka scan [path]
Scan project for security claims in code comments.
Arguments:
path— Directory or file to scan (default: current directory)
Options:
--format <type>— Output format:terminal(default),json, orsarif--severity <level>— Filter by severity:error,warning, orinfo--output <file>— Write results to file instead of stdout--config <path>— Path to custom config file--import <file>— Import SARIF file (repeatable)--min-confidence <level>— Filter by confidence:high,medium, orlow--verbose— Show detailed claim metadata and locations--quiet— Minimal output (errors only)
Examples:
# Scan current directory
uluka scan
# Scan specific directory with JSON output
uluka scan src/ --format json
# Save results to SARIF file for security tools
uluka scan . --format sarif --output uluka-report.sarif
# Show only high-severity findings
uluka scan --severity erroruluka verify [path]
Verify security claims against code implementation.
Arguments:
path— Directory or file to verify (default: current directory)
Options:
--llm— Enable Claude-powered verification (requiresANTHROPIC_API_KEY)--format <type>— Output format:terminal(default),json, orsarif--severity <level>— Filter by severity:error,warning, orinfo--output <file>— Write results to file instead of stdout--config <path>— Path to custom config file--import <file>— Import SARIF file (repeatable)--import-only— Only process imported findings, skip native scanning--min-confidence <level>— Filter by confidence:high,medium, orlow--verbose— Show verification evidence and LLM insights--quiet— Minimal output (errors only)--incremental— Use cached results, only re-analyze changed files--cache-dir <path>— Custom cache directory path
Examples:
# Verify claims in current directory
uluka verify
# Enable LLM-powered verification
export ANTHROPIC_API_KEY="sk-ant-..."
uluka verify --llm
# Verify with detailed evidence
uluka verify src/ --verbose
# Generate JSON report for CI/CD
uluka verify . --format json --output verification-report.jsonuluka init
Create a .ulukarc configuration file via interactive wizard.
Options:
--yes— Skip confirmation prompts (use defaults)
Example:
uluka initGuides you through configuring:
- LLM verification settings
- Rule severity customization
- API key storage preferences
- Output format defaults
uluka config validate
Validate .ulukarc configuration file syntax and schema.
Example:
uluka config validateChecks for:
- Valid YAML/JSON syntax
- Required fields present
- Correct data types
- Valid enum values
uluka cache clear
Clear the incremental analysis cache.
Example:
uluka cache clearuluka cache status
Show cache statistics (file count, total size, directory).
Example:
uluka cache statusuluka hook scan [path]
Scan files and output structured JSON for hook consumption.
Options:
--files <paths...>— Specific files to scan--incremental— Use cached results for unchanged files--cache-dir <path>— Custom cache directory path
Example:
uluka hook scan src/ --files src/auth.ts src/db.tsuluka hook pre-commit [path]
Pre-commit hook that blocks on critical findings.
Options:
--force— Report findings but do not block commit
Example:
uluka hook pre-commitLLM-Powered Verification
Uluka can optionally use Claude (Anthropic's AI) for nuanced claim analysis and verification of complex security properties that are difficult to verify with deterministic rules alone.
How to Enable
Use the --llm flag with the verify command:
uluka verify --llmAPI Key Requirement
LLM verification requires an Anthropic API key. Set the ANTHROPIC_API_KEY environment variable:
export ANTHROPIC_API_KEY="sk-ant-api03-..."Where to get an API key:
- Visit the Anthropic Console
- Sign up or log in
- Navigate to API Keys section
- Create a new API key
Alternatively, store your API key in .ulukarc (see Configuration section below).
Cost Considerations
LLM verification is opt-in via the --llm flag. Without this flag, Uluka uses only deterministic rule-based verifiers with zero API costs.
When enabled:
- Deterministic verifiers run first (free, instant)
- LLM provides supplemental insights for rule-verified claims
- LLM handles claims that don't match any deterministic verifier
- Typical cost: $0.01-0.05 per medium-sized file analyzed
How It Works
- Deterministic-first approach: Rule-based verifiers (EncryptionVerifier, AuthVerifier, etc.) run first and are the source of truth
- Supplemental insights: LLM reviews rule-verified claims and provides additional context or warnings
- Catch-all verification: Claims that don't match any rule-based verifier are analyzed by the LLM
- Graceful degradation: If LLM times out or fails, rule-based results are still returned with a warning
Example output with --llm --verbose:
✓ Encryption claim verified
Evidence: crypto.createCipheriv('aes-256-gcm', key, iv)
LLM Insight: Verified AES-256-GCM usage. Note: IV generation appears deterministic
(line 45). Consider using crypto.randomBytes() for each encryption.Configuration
Uluka looks for configuration in .ulukarc, uluka.config.yml, or uluka.config.json in your project root.
Example Configuration (YAML)
# .ulukarc or uluka.config.yml
anthropicApiKey: "sk-ant-api03-..." # Optional if env var is set
llm:
enabled: false # Default: false (opt-in via --llm flag)
timeout: 30000 # Timeout in milliseconds (30 seconds)
rules:
disabled: [] # List of rule IDs to disable
severity:
hardcoded-secret: error # Override severity for specific rules
weak-crypto: warning
missing-auth: error
output:
format: terminal # Default output format
verbose: false # Show detailed evidence by default
integrations:
trustedSources: # Tools whose findings are fully trusted
- semgrep
- codeql
imports: # Auto-import these SARIF files on every run
- path: ./reports/semgrep.sarif
tool: semgrep
discovery:
enabled: true # Enable automatic claim discovery
minConfidence: medium # Minimum confidence for discovered claimsExample Configuration (JSON)
{
"anthropicApiKey": "sk-ant-api03-...",
"llm": {
"enabled": false,
"timeout": 30000
},
"rules": {
"disabled": [],
"severity": {
"hardcoded-secret": "error",
"weak-crypto": "warning"
}
},
"output": {
"format": "terminal",
"verbose": false
}
}Configuration Precedence
Settings are resolved in this order (highest priority first):
- CLI flags —
--llm,--format,--severity, etc. .ulukarcfile — Project-specific configuration- Environment variables —
ANTHROPIC_API_KEY - Defaults — Built-in sensible defaults
Example: If .ulukarc sets format: json but you run uluka verify --format terminal, the CLI flag wins and output will be in terminal format.
Generating Configuration
Use the interactive wizard to create a config file:
uluka initThis guides you through all options and creates a validated .ulukarc file.
Programmatic Usage
Uluka can be used as a library in your Node.js or TypeScript projects.
Basic Example
import { VerificationEngine, type Claim, type UlukaConfig } from 'uluka';
const config: UlukaConfig = {
llm: { enabled: false }
};
const engine = new VerificationEngine(config);
const claim: Claim = {
type: 'encryption',
description: 'uses AES-256-GCM encryption',
location: {
file: 'src/auth.ts',
line: 42,
column: 5
},
metadata: {}
};
const result = await engine.verify(claim);
console.log(result.status); // 'verified' | 'failed' | 'inconclusive'
console.log(result.evidence); // Array of evidence objects
console.log(result.llmInsight); // Optional LLM supplemental analysisTypeScript Type Definitions
Uluka includes full TypeScript type definitions. Key types:
import type {
Claim,
VerifiedClaim,
VerificationStatus,
CodeLocation,
UlukaConfig,
ValidatedConfig,
VerificationReport,
Finding
} from 'uluka';Advanced Usage
import { ClaimVerifier, ConfigSchema } from 'uluka';
// Validate config with Zod schema
const validatedConfig = ConfigSchema.parse(rawConfig);
// Create custom verifier
const verifier = new ClaimVerifier(validatedConfig);
const claims = [/* ... */];
for (const claim of claims) {
const verified = await verifier.verify(claim);
// Handle result...
}Examples
Example 1: Verifying Encryption Claims
Code with security claim:
/**
* Encrypts user data before storage.
* @security-claim encryption: "uses AES-256-GCM encryption"
*/
export async function encryptData(data: string): Promise<Buffer> {
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
return Buffer.concat([cipher.update(data, 'utf8'), cipher.final()]);
}Verification result:
$ uluka verify src/encryption.ts --verbose
✓ Encryption claim verified
Location: src/encryption.ts:12
Evidence:
- Found crypto.createCipheriv call at line 14
- Algorithm: aes-256-gcm (matches claim)
- Key size: 256 bits (verified)Example 2: Catching Broken Authentication
Code with outdated claim:
/**
* Protected admin endpoint.
* @security-claim authentication: "requires JWT with RS256 signature"
*/
app.post('/admin/users', (req, res) => {
// TODO: Add auth middleware
const users = db.getAllUsers();
res.json(users);
});Verification result:
$ uluka verify src/api.ts
✗ Authentication claim FAILED
Location: src/api.ts:8
Issue: No JWT verification found in request handler or middleware chain
Severity: errorExample 3: LLM-Powered Insight
Code with complex security claim:
/**
* @security-claim authorization: "enforces RBAC with hierarchical permissions"
*/
function checkPermission(user: User, resource: string): boolean {
return user.roles.some(role =>
permissions[role]?.includes(resource)
);
}Verification result with LLM:
$ uluka verify src/auth.ts --llm --verbose
✓ Authorization claim verified
Evidence: Role-based access check found with permissions lookup
LLM Insight: RBAC implementation found, but hierarchical permission inheritance
is not implemented. Current logic only checks direct role-to-resource
mappings without parent role resolution.License
MIT License
Copyright (c) 2026 Uluka Contributors
See LICENSE file for full license text.
Contributing
Contributions are welcome! Please:
- Open an issue to discuss significant changes
- Ensure all tests pass:
npm test - Follow existing code style and patterns
- Add tests for new features
Support
- Issues: GitHub Issues
- Documentation: This README and inline TypeScript types
- API Reference: Generated TypeScript declarations in
dist/index.d.ts
Built with TypeScript, powered by Claude AI (optional), designed for security-conscious developers.
