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

@odla-ai/security

v0.3.1

Published

Evidence-gated vulnerability harness for odla and Cloudflare apps: passive rules, checkpointed recon/hunt, app-attributed hosted independent validation, baselines, and SARIF.

Readme

@odla-ai/security

A model-agnostic vulnerability harness for odla and Cloudflare applications. It keeps reconnaissance, hunting, validation, evidence, and reporting separate; checkpoints progress outside model context; and refuses to call a lead a confirmed vulnerability until code-enforced gates pass.

This package is informed by Cloudflare's Build your own vulnerability harness and its released security-audit seed skill. The article links the seed skill, not production-harness source. @odla-ai/security is an independent, smaller implementation of the published lessons, not a port or a claim to Cloudflare's fleet results.

What ships in 0.2

  • Passive JavaScript/TypeScript rules in generic, plus Cloudflare Wrangler and odla inbound-trust-header checks in the derived profiles.
  • Immutable repository snapshots with content/manifest digests, capture flags, Git provenance, and fail-closed size limits. Truncated snapshots are rejected.
  • A checkpointed Recon → Hunt → Validate → Report state machine.
  • Line-shift-resistant root-cause fingerprints that stores can index across runs, plus reasoned, expiring baselines. The bundled stores do not query a fleet-wide canonical-finding index yet.
  • Optional discovery and validation roles over any structural @odla-ai/ai-compatible extract() facade.
  • Mechanical path, line, trace, threat-model, reproduction, and source-integrity checks in ordinary code.
  • An explicit isolated-executor seam. There is deliberately no host-shell fallback.
  • JSON for replay, Markdown for humans, and SARIF 2.1.0 for code scanning.
  • A passive odla-security CLI for local and credential-free CI scans.

The deterministic rules emit candidates, not confirmed vulnerabilities. They are a cheap way to seed deeper hunts and catch a narrow set of high-signal mistakes. A clean run does not prove a codebase is secure, and this project does not claim recall on real repositories.

Install and scan

Availability: first require npm view @odla-ai/[email protected] version to succeed. An exact-version E404 means this release is unavailable; it does not prove the package name itself is absent. Do not install or treat the gate as passed. Inside this repository use npm run security:scan.

npm i -D --save-exact @odla-ai/[email protected]
npx odla-security scan . \
  --profile odla \
  --out .odla/security/latest \
  --fail-on high \
  --fail-on-candidates critical

Use --profile odla for applications built with odla; it includes the generic and Cloudflare checks plus odla trust-boundary rules. Use --profile cloudflare-app for Cloudflare applications that do not use odla.

CLI options

| Option | Meaning | | --- | --- | | [target] | Directory to snapshot; defaults to .. | | --profile generic\|cloudflare-app\|odla | Select passive rules and hunt taxonomy. | | --out <directory> | Private JSON, Markdown, SARIF, and checkpoint directory. | | --run-id <id> | Correlation ID; matching snapshot/config replays untrusted local stages. | | --baseline <json> | Accountable fingerprint suppressions with owner, reason, and expiry. | | --fail-on <severity> | Gate confirmed findings; defaults to high. | | --fail-on-candidates <severity> | Gate unconfirmed leads; defaults to critical. | | --no-fail-on-candidates | Explicitly disable the passive-lead gate. | | --allow-incomplete | Accept incomplete autonomous coverage after review. | | --exclude <path> | Add a repeatable snapshot exclusion. |

Credential-free CI can run the same passive gate and retain reviewed artifacts:

- run: npm ci --no-audit --no-fund
- run: npx odla-security scan . --profile odla --out .odla/security/ci --fail-on high --fail-on-candidates critical
- if: always()
  uses: actions/upload-artifact@v6
  with:
    name: odla-security-passive
    path: |
      .odla/security/ci/REPORT.md
      .odla/security/ci/report.json
      .odla/security/ci/report.sarif
    retention-days: 14

report.sarif is suitable for a code-scanning integration, but unconfirmed leads are intentionally emitted at note level. Review reports before granting CI artifacts broader access.

The CLI never calls a model, executes target code, enables a network, writes to production, or fixes source. It writes:

  • report.json — complete run, coverage, rejected candidates, and evidence;
  • REPORT.md — status-aware human report;
  • report.sarif — code-scanning interchange;
  • state/*.json — atomic private stage checkpoints for audit and diagnostics.

The Node writers create artifacts mode 0600, replace leaves atomically, and reject symlinks in output/state path components instead of following them out of the requested directory. Local checkpoints are intentionally unauthenticated in 0.2, so a later process replays every security-critical stage instead of trusting stored candidates, coverage, decisions, or evidence. This prevents checkpoint edits from creating confirmations or clean false negatives; authenticated distributed receipts are the next persistence increment.

--fail-on high gates only confirmed, unsuppressed findings. Passive CLI scans cannot produce confirmed findings because they configure no validator or executor. Critical candidate gating is on by default; --no-fail-on-candidates is the explicit opt-out. odla uses the critical tier for credential-shaped leads without letting noisy high-severity heuristics block every build.

Use platform-managed AI for autonomous hunts

Hosted odla runs exchange an ordinary app-owner developer credential for two short-lived, role-bound inference grants. Provider keys remain in odla's platform vault, the platform chooses the admin-configured discovery and validation models, and each response must carry a matching run/purpose/role/provider/model receipt before the harness accepts it:

npx @odla-ai/cli security run . --env dev --ack-redacted-source

For autonomous SaaS operation, let odla.ai acquire a connected GitHub source server-side instead of uploading a local snapshot:

npx @odla-ai/cli security github connect --env dev # Contents/Metadata read; optional Checks write
npx @odla-ai/cli security plan --env dev
npx @odla-ai/cli security sources --env dev
npx @odla-ai/cli security run --source <source-id> --ref main --env dev --plan-digest <digest-from-security-plan> --ack-redacted-source

The server verifies the requested repository, resolves the ref to a commit SHA, and keeps source inside one bounded acquire/analyze stage. No raw archive, file body, or explicit source excerpt is deliberately persisted in D1, hosted report storage, Workflow checkpoints, logs, or GitHub Checks. The private normalized report may retain bounded best-effort credential-pattern-redacted model-derived prose and repository-relative paths for up to 90 days; treat it as sensitive because those values may reveal source semantics. GitHub/provider terms remain separate.

GitHub installation is read authorization, not disclosure consent, so the redacted-source acknowledgement remains mandatory. security plan shows the exact discovery/validation provider/model routes, policy revisions and bounds, prompt bundle, redaction/report contracts, readiness, retention, and no-execution boundary before consent. A job requires --plan-digest and binds the acknowledgement to that complete digest; any covered change requires a refreshed plan and acknowledgement. The CLI never asks for a PAT, installation id, provider key, or provider/model selection override; it deliberately shows the read-only route identities in the consent preflight.

The archive envelope is 16 MiB compressed, 48 MiB decompressed, 20,000 entries, 10,000 files, 2 MiB per file, and 16 MiB aggregate file bytes. Links, special entries, unsafe redirects, duplicate paths, and escaping paths are rejected. The acquire/analyze step has zero automatic retries; a failed job is terminal and must be explicitly rerun after correcting the cause. Source ids belong to one app/environment. The App is not subscribed to Push and ignores one if unexpectedly delivered. Disconnect cancels nonterminal work while terminal reports remain until normal expiry. Source jobs default to cloudflare-app; local-path jobs default to odla.

The CLI follows and gates the job by default. Optional Checks write permission produces a best-effort fixed summary linking to Studio; a Check is not the CLI gate. Target code is not executed, so without an injected isolated executor the hosted pass cannot mechanically confirm a finding. Critical candidates and incomplete/shallow/blocked/unscheduled/budget-exhausted work are the operative signals and cannot be presented as proof of security.

The local-snapshot command handles device authorization and app/run attribution; it never asks for a provider key. Its only network peer is the configured odla platform origin. It sends bounded typed operations to /registry/ai/extract; the odla.ai Worker—not this package—owns the security system prompts and output schemas, selects the admin-configured provider/model, resolves the vaulted credential, calls the provider, records o11y/usage, and returns a role-bound receipt. The lower-level factory below is for embedding the same flow in another orchestrator.

import {
  createPlatformSecurityReasoners,
  createSecurityHarness,
  odlaProfile,
  securityFingerprint,
} from "@odla-ai/security";
import { FileRunStore, snapshotDirectory } from "@odla-ai/security/node";

const snapshot = await snapshotDirectory(".");
const hosted = await createPlatformSecurityReasoners({
  platform: "https://odla.ai",
  token: process.env.ODLA_DEV_TOKEN!,
  appId: "my-app",
  env: "dev",
  repository: snapshot.repository,
  revision: snapshot.revision,
  snapshotDigest: snapshot.digest,
  clientRunId: crypto.randomUUID(),
  sourceDisclosure: "redacted",
});

const harness = createSecurityHarness({
  profile: odlaProfile(),
  store: new FileRunStore(".odla/security/state"),
  discoveryReasoner: hosted.discoveryReasoner,
  validationReasoner: hosted.validationReasoner,
});

const report = await harness.run(snapshot, { runId: hosted.run.runId });
await hosted.complete({
  reportDigest: await securityFingerprint(report),
  coverageStatus: report.coverageStatus,
  confirmed: report.metrics.confirmed,
  candidates: report.metrics.candidates,
});

Opaque inference grants stay inside the returned reasoner closures and are not exposed in run metadata. Starting a run is attributed to the named app and environment; odla's engineering environment additionally sends an authorized self-audit service identity through a fresh platform:security:self approval. odla-ai security run defaults to 12 fairly sampled hunt tasks, writes private artifacts under .odla/security/hosted (or --out), keeps source disclosure redacted, and leaves active reproduction off. --ack-redacted-source is the explicit approval after reviewing the configured providers' retention and residency terms. Platform admins, not the local process, select the two models and their immutable call budgets. Hosted reasoners advertise those ceilings to the harness: reconnaissance consumes one discovery call, first-pass hunts are fairly capped to the remainder, and only calls left after first passes may fund shallow retries. Validation spends its separate grant on vulnerability candidates in severity, kind, confidence, and reproduction-readiness order. Skipped hunts become budget_exhausted coverage cells; skipped validations remain candidate with validationStatus: "budget_exhausted". The report's callBudget counts make either gap explicit and force coverageStatus to incomplete. Caller-managed reasoners that do not advertise callBudget keep the existing caller-defined scheduling behavior. Prior local state never supplies candidates, coverage, decisions, or confirmation authority. The lower-level factory caller must write/digest its report and invoke hosted.complete(...); that capability uses a separate one-time grant and never reuses the credential which started the run. For hosted calls, maxInputBytes measures the decoded dynamic input field; the immutable server-owned prompt/tool contract is protected by a separate wire-size ceiling and does not silently consume that advertised budget. maxOutputTokens is a hard cap over the reasoners' operation defaults (4,096 for recon, 8,192 for hunt, and 4,096 for validation), not a request to spend the entire configured maximum on every call. Optional local limits can only narrow those server bounds. These are call-count and per-call request bounds, not a separate aggregate per-run token or USD-spend ceiling.

The platform writes a metadata-only reserved usage row before each provider call, then finalizes it to success or error; reservations left open by a Worker interruption become abandoned after one hour. Fixed early-access rolling 24-hour ceilings are 3 runs per app/environment, 6 per owner, 20 in the customer pool, and 3 in odla's separate self-audit pool. A platform-ceiling 429 always carries a conservative Retry-After. A provider-side 429 uses the sanitized provider_rate_limited code after the provider SDK's bounded retries and carries a bounded Retry-After only when the upstream supplied one. The broker does not multiply retries or silently change the admin-selected model. Daily retention removes each run, grant, and usage record after 90 days from its own creation, up to one sweep interval. Provider retention and residency are separate and still require the disclosure acknowledgement.

For custom or entirely local orchestration, the lower-level adapters remain structurally typed: createAiDiscoveryReasoner and createAiValidationReasoner accept any compatible extract() facade. That path is explicitly caller-managed; it does not provide platform attribution, opaque grants, or receipt verification.

Discovery and validation are different capabilities. A validator can return only confirmed, rejected, or needs_reproduction; it has no API for filing a new finding. With the default requireIndependentModel: true, a two-model run must declare provider and model for both roles; the harness rejects missing identity data, the same reasoner identity, or the same provider on both sides. Hosted adapters verify the platform receipt on every call. On a custom facade, resolveProvider additionally checks each declared provider against the actual model router; without a resolver, provider identity is caller-attested and must not be treated as cryptographic proof of independence. Adapter identities, models, and methods are captured and frozen at construction. Setting requireIndependentModel: false can permit a non-independent experiment to run, but it can never promote that run's output to confirmed.

Source passed to a reasoner is line-numbered, digest-labelled, bounded, and explicitly marked as untrusted. The default modelSourceDisclosure: "redacted" withholds untracked, provenance-unknown, and sensitive-path files and redacts credential-shaped spans; "full" is an explicit provider-disclosure opt-in. This reduces secret exposure and prompt-injection risk but does not make source trustworthy or override a model provider's retention/residency terms. Model-proposed commands are inert data unless the host separately enables active mode and injects an isolated executor.

Redacted validation also replaces every free-text candidate field, reproduction argument/expectation, and stdout/stderr body with structural metadata or byte counts before it crosses to a validator. This prevents a custom rule from smuggling text copied from a withheld file. AI adapters cap the complete dynamic prompt with maxInputBytes (128 KiB by default), including metadata, candidate, and evidence—not only source context. Full disclosure is the only mode that passes candidate theory text, and execution output remains best-effort credential-pattern-redacted even there.

The confirmation gate

confirmed means all of these held:

  1. The candidate names a concrete attacker, crossed boundary, action, and impact.
  2. Plain code verifies every cited repository-relative path and one-based line, and enforces entrypoint → propagation* → sink ordering.
  3. A configured isolated executor observes the proposed reproduction against the exact snapshot digest.
  4. The executor attests to read-only source, returns the same post-run digest, and plain code compares command arguments, exit status, stdout/stderr expectations, normalized working directories, time, and output budgets.
  5. A separate validation reasoner tries to disprove the trace, reachability, mitigations, runtime assumptions, impact, and evidence.

Without an independent validator, output stays candidate. Without clean execution evidence, a validator's positive judgment stays needs_reproduction. A validator's rejection is advisory and remains visible as a candidate unless deterministic checks disprove the source trace. Structural schema validity alone can never confirm a finding.

Active execution is fail closed

The package does not ship a local process executor. To set policy.active: true, supply an IsolatedExecutor whose isolation is container, vm, or remote-sandbox and whose source is read-only. The harness rejects active mode without it and rejects evidence if source changes.

The executor is responsible for a disposable environment with no host secrets, cloud metadata, Docker socket, SSH agent, or production credentials; bounded CPU, memory, disk, process, time, and output; and network disabled unless the run policy explicitly opts in. A container label alone is not proof of a safe sandbox.

The harness bounds and redacts stdout/stderr and argument values before persistence or validation model calls. Executor evidence is materialized once, so validation and persistence cannot observe different accessor-controlled values. Unsupported evidence fields are rejected and omitted; external executor/reasoner error text is also redacted and byte-bounded. Artifacts are still security-sensitive; do not publish them without review.

Baselines are accountable and temporary

{
  "version": 1,
  "entries": [{
    "fingerprint": "sha256:0000000000000000000000000000000000000000000000000000000000000000",
    "reason": "Accepted while the legacy endpoint is removed in SEC-42",
    "owner": "platform-security",
    "expiresAt": "2026-08-01T00:00:00.000Z"
  }]
}

Missing owners, reasons shorter than eight characters, malformed fingerprints/timestamps, and duplicate entries fail the run. Expired entries stop suppressing automatically. Rejected candidates are retained in JSON so repeated scans can measure noise instead of forgetting it.

Persistence and Cloudflare

MemoryRunStore works in tests and short Worker invocations. FileRunStore atomically records local/CI stages without following state or directory symlinks. These unauthenticated files are audit records, not trusted resume authority. Distributed Cloudflare operation should implement RunStore over D1 or Durable Objects and schedule each coverage cell with Cloudflare Workflows (or @odla-ai/workflows) using leases and idempotency keys. Process execution remains a remote Node/VM concern; Workers are orchestrators, not Linux sandboxes.

Node subpath

Node-only filesystem APIs live at @odla-ai/security/node; keeping them on a separate entrypoint preserves a Worker-compatible root API:

snapshotDirectory(root: string, options?: SnapshotDirectoryOptions): Promise<RepositorySnapshot>
new FileRunStore(directory: string): FileRunStore
writeSecurityArtifacts(directory: string, report: SecurityReport): Promise<void>

snapshotDirectory resolves Git metadata from repository subdirectories and worktrees. Untracked or provenance-unknown files are still scanned by passive rules but withheld from model context by default. An output directory inside the target is automatically excluded by the CLI so a repeated run does not scan its own prior artifacts.

Step 9 roadmap

The minimum trustworthy single-repository harness is implemented first. Next:

  1. Authenticated D1/DO task leases, signed attempt receipts, heartbeats, and artifact storage so distributed runs can resume without trusting editable local JSON.
  2. Versioned attack/prompt bundles and held-out seeded-vulnerability evals.
  3. Coverage gapfill and narrowly seeded sibling hunts driven by evidence, not every zero-finding task.
  4. Deterministic candidate indexes followed by model-assisted dedup only when volume warrants it.
  5. Dependency graph and cross-repository reachability after more than one repository matters.
  6. Patch proposals and fail-before/pass-after regression evidence on isolated overlays. Humans remain the only merge/deploy authority.

Feedback will create new immutable prompt versions; it will never silently rewrite the prompt attached to an in-flight or historical task.

Current limitations

  • The passive rules are regex-based candidate generators, not parsers or SAST.
  • FileRunStore is single-host persistence, not a distributed queue.
  • There is no bundled sandbox, auto-fixer, cross-repo tracer, or production reachability system in 0.1.
  • Multiple runs may find different code paths. A zero-finding task is not automatically marked shallow; explicit task-health evidence is required.
  • A coverage cell marked complete means its bounded task completed. Areas that do not fit one context remain shallow with a chunking wishlist item, and cells beyond the task budget remain explicitly unscheduled.
  • Reconnaissance areas must resolve to an exact repository-relative file or directory (or .). Descriptive/unmapped labels do not fall back to the full repository; they remain shallow with an explicit mapping wishlist item.
  • SARIF includes candidates with their disposition so consumers must not render every result as confirmed.

See the installed package's exported TypeScript declarations/JSDoc or the rendered reference at https://odla.ai/docs/packages/security for the root API surface, and the Node-subpath section above for filesystem adapters.