@ezducate/codelytra

npm

9 min read Original article ↗

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.json

Then fail CI only for new findings at or above the configured policy:

npx codelytra ci . \
  --baseline .codelytra/baseline.json \
  --sarif .codelytra/codelytra.sarif \
  --no-color

3. 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.json

4. 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 .codelytra directory with owner-only permissions where supported. In CI, prefer CODELYTRA_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

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.