Codelytra
AI application security from code to agent action.
Codelytra is a local-first security auditor for JavaScript and TypeScript applications that use LLMs, AI agents, tool calling, retrieval-augmented generation, or MCP. It follows supported untrusted and sensitive data flows through model boundaries to privileged actions, produces evidence-rich attack paths, exports SARIF, and optionally uses any OpenAI-compatible model to explain deterministic findings.
- No account, API key, network access, or telemetry for static scans.
- Target repositories are parsed, never imported, installed, built, or executed.
- Severity and confidence are reported separately.
- Optional dynamic tests are bounded and restricted to explicitly configured targets.
- Optional AI explanations enrich evidence; they never create or remove deterministic findings.
Requirements and installation
Codelytra requires Node.js 22 or newer.
npm install --save-dev @ezducate/codelytra
npx codelytra init
npx codelytra scan .During an interactive installation, Codelytra may offer to configure an optional OpenAI-compatible provider. Skip it to remain fully offline. In CI and non-interactive installs, setup is skipped automatically. To suppress the install-time offer explicitly:
CODELYTRA_SKIP_AI_SETUP=1 npm install --save-dev @ezducate/codelytra
Thirty-second example
Given:
const response = await openai.responses.create({ model: 'example-model', input: req.body.message, }); exec(response.output_text);
Codelytra reports:
CRITICAL / HIGH CONFIDENCE
CL-AI-001 Model output reaches command execution
src/app.ts:11
req.body.message [untrusted, user_controlled]
-> openai.responses.create(...) [model_generated]
-> exec(response.output_text) [command execution]
The report explains why the path matters, identifies mitigations found on the path, maps the finding to OWASP GenAI/CWE where appropriate, and recommends a narrow deterministic capability instead of model-produced shell commands.
CLI reference
Syntax uses <required> and [optional]. Relative paths are resolved from the current directory.
Global options
| Option | Meaning |
|---|---|
-V, --version |
Print the installed version. |
--no-color |
Disable terminal color. NO_COLOR is also respected. |
--quiet |
Suppress ordinary terminal output; requested files are still written. |
--debug |
Include diagnostic details where supported, including CI parse diagnostics. |
-h, --help |
Show help for a command. |
Global options go before the command, for example codelytra --no-color scan ..
Commands
| Command | Arguments and options | Purpose |
|---|---|---|
codelytra version |
None | Print the semantic version. |
codelytra init |
-y, --yes; --force
|
Create codelytra.yaml, update .gitignore, and optionally configure smart features. --yes accepts offline defaults. --force replaces an existing config. |
codelytra scan [path] |
See scan options below | Run static analysis. path defaults to .. |
codelytra sast [path] |
Same as scan
|
Alias for scan. |
codelytra ci [path] |
Same as scan
|
Run with stable policy exit codes for CI. |
codelytra report [path] |
Same as scan
|
Scan and render the selected report format. |
codelytra inventory [path] |
-o, --output <file> |
Emit the versioned AI Security Manifest as JSON. |
codelytra graph [path] |
-f, --format <text|json|mermaid|dot>; -o, --output <file>
|
Export the attack-path graph. |
codelytra mcp [path] |
--json |
Show recognized MCP servers and tool risk classifications. |
codelytra rules list |
None | List stable built-in rule IDs, severities, and titles. |
codelytra rules show <rule-id> |
Required rule ID | Print a rule's structured metadata and remediation. |
codelytra explain <finding-id> |
--report <file>; --ai; --dry-run
|
Explain a finding from a JSON report. The default report is .codelytra/report.json. --ai calls the configured provider. --dry-run shows metadata that would be sent without making a call. |
codelytra fix <finding-id> |
--report <file>; -o, --output <file>
|
Write a non-applying remediation proposal. The default is .codelytra/patches/<finding-id>.md. |
codelytra provider setup |
Interactive | Store an OpenAI-compatible base URL, model, and hidden API-key input in the user's private Codelytra directory. |
codelytra provider doctor |
None | Test authentication and the chat-completions response shape without printing the key. |
codelytra audit --target <name> |
Required --target; optional --authorized, --json
|
Run bounded semantic tests against a configured target. dast is an alias. Remote targets require explicit authorization. |
Scan, CI, and report options
| Option | Values/default | Behavior |
|---|---|---|
[path] |
Default .
|
Repository or workspace root to analyze. |
-f, --format <format> |
text (default), json, sarif, markdown, html
|
Select stdout or --output representation. |
-o, --output <file> |
No default | Write the selected format to a specific file. |
--json |
Boolean | Emit JSON only to stdout; shorthand for machine-readable output. |
--sarif <file> |
No default | Also write a SARIF 2.1.0 report at the supplied path. |
--baseline <file> |
No default | Mark matching stable fingerprints as suppressed. |
--write-baseline <file> |
No default | Write current finding fingerprints for later comparison. |
--no-write |
Boolean | Do not create the default .codelytra/report.json and .codelytra/report.sarif. |
--profile |
Boolean | Write total elapsed time, analyzed-file count, and diagnostic count to stderr. |
Without --no-write, a scan writes JSON and SARIF under .codelytra/ even when terminal output uses another format.
For the complete command-by-command reference, see docs/cli.md.
Realistic usage scenarios
1. Review a local AI service
npx codelytra init --yes npx codelytra scan . --format text npx codelytra graph . --format mermaid --output .codelytra/attack-paths.mmd npx codelytra inventory . --output .codelytra/manifest.json
Use the finding ID printed by the scan:
npx codelytra explain CL-AI-001-a493a694 npx codelytra fix CL-AI-001-a493a694
2. Gate pull requests while baselining accepted findings
Create and review a baseline on the main branch:
npx codelytra scan . --write-baseline .codelytra/baseline.json
git add .codelytra/baseline.jsonThen fail CI only for new findings at or above the configured policy:
npx codelytra ci . \
--baseline .codelytra/baseline.json \
--sarif .codelytra/codelytra.sarif \
--no-color3. Scan a monorepo component
npx codelytra scan apps/support-agent --format markdown --output artifacts/support-agent.md
npx codelytra scan packages/mcp-server --json --no-write > artifacts/mcp-server.json4. Investigate a RAG-to-tool path
npx codelytra scan . --format text npx codelytra graph . --format dot --output .codelytra/graph.dot npx codelytra mcp . --json
Review whether retrieved documents influence external email, refunds, filesystem mutation, command execution, or unrestricted requests, and whether deterministic authorization and context-specific validation exist.
5. Add an optional smart explanation
npx codelytra provider setup npx codelytra provider doctor npx codelytra explain CL-AI-012-8ac1 --ai --dry-run npx codelytra explain CL-AI-012-8ac1 --ai
--dry-run includes no provider request. AI explanations receive structured finding evidence, pass through secret redaction, and must satisfy a strict response schema.
6. Safely test a developer-owned local endpoint
Add a target to codelytra.yaml:
version: 1 targets: local-chat: url: http://localhost:3000/api/chat method: POST headers: content-type: application/json authorization: Bearer ${TEST_APP_TOKEN} request: body: message: '{{prompt}}' response: textPath: '$.message' toolPath: '$.testTrace.tools' limits: maxRequests: 10 concurrency: 1 timeoutMs: 20000 maxResponseBytes: 1000000 authorization: confirmed: false
Run:
TEST_APP_TOKEN=test-only-token npx codelytra audit --target local-chat
For a non-loopback target, add --authorized only when you own the target or have explicit authorization:
npx codelytra audit --target staging-chat --authorized --json
The audit does not crawl, scan ports, guess credentials, use real secrets, or attempt destructive exploitation.
Configuration
Codelytra reads the first available codelytra.yaml, codelytra.yml, or codelytra.json. Config is parsed as data and validated strictly; executable target config is never loaded.
version: 1 scan: include: - src/**/*.ts - src/**/*.tsx - src/**/*.js - src/**/*.jsx exclude: - node_modules/** - dist/** - build/** - coverage/** - '**/*.d.ts' maxFileBytes: 2000000 followSymlinks: false policy: failOn: severity: high confidence: medium failOnIncomplete: false requireSuppressionReason: true rules: disabled: [] severityOverrides: CL-AI-020: low trust: approvedModelHosts: - api.openai.com - api.anthropic.com sensitiveEnvPatterns: - '*_API_KEY' - '*_TOKEN' - '*_SECRET' - '*_PASSWORD' targets: {}
See docs/configuration.md for every field, valid value, default, and target-safety rule.
Suppression and baselines
Inline suppression applies to the next line:
// codelytra-ignore-next-line CL-AI-004 -- restricted by the outbound gateway allowlist await fetch(modelSelectedUrl);
With the default policy, high/critical suppressions without a reason are ignored. Suppressions remain visible in JSON and SARIF metadata. Baselines use stable fingerprints based primarily on rule, relative file, containing symbol, sink/source kind, and path shape—not line number alone.
Reports and exit codes
Formats:
- Text: readable terminal findings and attack paths.
- JSON: stable schema version
1.0. - SARIF: GitHub-compatible SARIF 2.1.0.
- Markdown: CI artifact or pull-request summary.
- HTML: self-contained, escaped report with no remote scripts.
- Graph: text, JSON, Mermaid, or DOT.
- Manifest: versioned AI Security Manifest JSON.
Exit codes:
| Code | Meaning |
|---|---|
| 0 | Analysis completed and policy passed. |
| 1 | Findings met the configured failure threshold, or a dynamic test failed. |
| 2 | Invalid CLI usage or configuration. |
| 3 | Analysis failure. |
| 4 | Reserved for dynamic-target configuration failure. |
| 5 | Explicitly required provider operation failed. |
| 6 | Analysis was incomplete while failOnIncomplete: true. |
GitHub Actions
name: Codelytra on: pull_request: push: branches: [main] jobs: codelytra: runs-on: ubuntu-latest permissions: contents: read security-events: write steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 - run: npm install --save-dev @ezducate/codelytra - run: npx codelytra ci . --sarif .codelytra/codelytra.sarif - uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: .codelytra/codelytra.sarif
TypeScript SDK
import { createAuditor, defineConfig, defineRule } from '@ezducate/codelytra'; const config = defineConfig({ policy: { failOn: { severity: 'high', confidence: 'medium' }, failOnIncomplete: false, requireSuppressionReason: true, }, }); const auditor = createAuditor({ projectRoot: process.cwd(), config, }); const result = await auditor.scan({ signal: AbortSignal.timeout(60_000), baseline: '.codelytra/baseline.json', }); const manifest = await auditor.inventory(); const sarif = await auditor.report(result, { format: 'sarif' }); for (const finding of result.findings) { console.log(finding.ruleId, finding.severity, finding.confidence); }
Public exports are intentionally small: createAuditor, defineConfig, defineRule, VERSION, and the documented result/config/rule types. See docs/sdk.md.
Optional runtime observation helpers
import { markSensitive, markUntrusted, traceLlmCall, traceToolCall, observeToolCall, } from '@ezducate/codelytra/runtime';
These helpers are opt-in and do not affect static scanning. They attach metadata or emit events through a caller-supplied observer; they do not send telemetry.
Supported recognition and rules
Current recognition includes common patterns from:
- OpenAI SDK
- Anthropic SDK
- Vercel AI SDK
- LangChain.js
- Google GenAI
- MCP TypeScript SDK
- Express-like HTTP input, retrieval helpers, environment secrets, and Node.js high-impact sinks
The initial catalog contains 17 rules covering model-influenced command/code execution, raw SQL, unrestricted URLs, filesystem mutation, unsafe HTML, external destinations, authorization, approval, schema validation, privileged prompt channels, retrieved-content attack paths, sensitive provider input, structured output, provider control, unbounded loops, and consumption guards. Run codelytra rules list or see docs/rules/index.md.
Security and privacy
- Static scanning makes no network requests.
- No telemetry is collected.
- Target files are never executed.
- Symlinks outside the root are not followed by default.
- File sizes, graph passes, provider timeouts, response sizes, and dynamic request counts are bounded.
- HTML output escapes repository/model content and uses a restrictive CSP.
- Provider HTTP is allowed only on loopback; remote providers require HTTPS.
- Provider keys are stored outside the repository in the user's
.codelytradirectory with owner-only permissions where supported. In CI, preferCODELYTRA_AI_API_KEY. - Provider requests refuse redirects and never log authorization headers.
- Remote dynamic targets require explicit authorization.
Codelytra complements general SAST and software-composition analysis. It does not certify an application, detect every vulnerability, or guarantee prompt-injection resistance.
Documentation
- Getting started
- Complete CLI reference
- Configuration reference
- Usage scenarios
- SDK and runtime API
- Architecture
- Data flow
- Attack graph
- AI Security Manifest
- OpenAI-compatible provider
- Dynamic audit
- MCP security
- Privacy
- Rules
- Rule authoring
- Threat model
- Limitations
Contributing and disclosure
See CONTRIBUTING.md. Report vulnerabilities privately through GitHub Security Advisories as described in SECURITY.md.
Codelytra is licensed under Apache-2.0 and maintained by EZducate LLC.