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

@secureport/core

v2.3.0

Published

Shared domain model for Secureport. Imported by the hosted API the same way a third party would.

Readme

@secureport/core

The shared domain model for Secureport. The hosted API imports this package the same way a third party would — one model, one implementation.

MIT licensed. Zero runtime dependencies.

The model in one paragraph

A run produces findings. Findings are immutable evidence: one detection, in one run, with no status of its own. Each finding is fingerprinted and reconciled into an issue — the tracked record that persists across runs and carries status, severity, age and history. A snapshot is issue state as of a run, and is the only thing a report ever reads.

The tracked entity is the issue, not the finding. Two scanners reporting the same weakness produce one issue with two sources, not two rows; an issue that comes back after being fixed is a regression, not a discovery.

Install

npm install @secureport/core

What it does

Everything below is pure: no I/O, no clock, no randomness. Time and ids are arguments, which is what makes a report reproducible and a test a golden file.

import { buildSnapshot, importNuclei, renderMarkdown, parseSnapshot } from '@secureport/core';

const findings = importNuclei(text, { orgId, runId, targetId, now, newId });

const snapshot = buildSnapshot({
  target,
  run,
  findings,
  previous: parseSnapshot(lastTime), // optional — this is what makes a retest
  now,
  newId,
});

renderMarkdown(snapshot, { kind: 'retest', now });

| | | | ------------------ | -------------------------------------------------------------------------- | | Importers | importNuclei, importZap, importBurp, importNessus, importGeneric | | Fingerprint | fingerprint, vulnKey, normaliseLocation, FINGERPRINT_VERSION | | Reconciliation | reconcile — the only thing that changes issue state | | Migration | planRefingerprint — what a version bump would do, before it does it | | Coverage | coversLocation — what makes auto-resolution safe | | Snapshots | buildSnapshot, parseSnapshot | | Reports | renderMarkdown, renderHtml, renderJson, buildReportModel |

Rendered examples of all five report kinds are at secureport.io — the source repository is private, so the site is where they live.

For a command line rather than an API, see @secureport/cli:

npx @secureport/cli report pen scan.jsonl --url https://app.example.com

Status

Pre-1.0. Every export is a semver contract already, and the shapes are settled by five report templates and five importers built on them — but below 1.0.0 a minor may still move something. See the deprecation policy below for what changes at 1.0.0.

0.2.x had a placeholder reconcile() that matched issues by title. It is gone rather than deprecated — matching by title is not a simplified version of matching by fingerprint, it is a different and wrong answer.

Severity

Five levels, ordered: critical · high · medium · low · advisory.

Compare them with severityRank, never as strings — sorting the strings puts advisory first.

import { severityFromCvss, severityRank, SEVERITY_WEIGHTS } from '@secureport/core';

severityFromCvss(9.8); // 'critical'  — CVSS v3.1 rating scale, unchanged
severityFromCvss(0); // 'advisory'  — CVSS calls this "None"

severityRank('critical') > severityRank('high'); // true
SEVERITY_WEIGHTS.advisory; // 0 — advisories never inflate the exposure score

severitySource records why a severity was assigned — explicit, cvss, engine_default or published_advisory, in that precedence order. A severity with no provenance is not evidence.

published_advisory means a security bulletin said so. It is deliberately not called advisory: that is the name of the lowest severity, and a critical finding whose source read advisory was one word away from being skimmed past.

Remediation deadlines

sla_due_at derives from an issue's effective severity, and both helpers are pure — neither reads the clock.

import { slaDueAt, slaStatus, DEFAULT_SLA_POLICY } from '@secureport/core';

const due = slaDueAt('high', issue.firstSeen); // 30 days after firstSeen
slaStatus(due, asOf); // 'within' | 'due_soon' | 'breached'

Windows default to 7 / 30 / 90 / 180 days for critical through low, and advisory has no deadline at all — a zero-weight severity should not manufacture breaches. Pass your own SlaPolicy to override; the defaults are this package's opinion, not the domain's.

Deadlines run from firstSeen, which is never reset — so a regression does not give a year-old problem a fresh clock.

Reports

Every report template consumes a Snapshot and nothing else: no database handle, no network, no clock. That is what makes a report reproducible, testable against golden files, and renderable by someone with findings on disk and no account.

The snapshot is the artefact; a PDF is a view of it.

Public contracts

Every export is a semver contract. Two of them are worth stating separately, because they outlive any one version of this package.

fingerprint_version

An issue's identity is the pair (fingerprintVersion, fingerprint), not the fingerprint alone. FINGERPRINT_VERSION is fp_v2 and it travels with every fingerprint everywhere it is stored, so a fingerprint written today can always be interpreted — including by code that has moved on.

fingerprint = sha256(targetId + '|' + vulnKey + '|' + normalisedLocation + '|' + (parameter ?? port ?? ''))

vulnKey is the engine-independent name for a weakness: a mapping-table entry where one exists, then the CWE, then the category, then engine:ruleId. That chain is what makes two scanners reporting the same weakness one issue rather than two.

Changing any of this changes the version, and a version change means a migration that recomputes fingerprints while preserving history — firstSeen is never reset — not a silent recomputation that orphans every issue. That is the whole reason the version exists, and it is why it is a contract rather than an implementation detail.

The JSON report

renderJson emits a JsonReport, versioned by reportVersion (currently 1), for consumers rendering their own view. It carries the derived answers beside the snapshot rather than only the snapshot, and two of its fields are there for a specific reason:

  • basis — how the findings were produced, as data. A consumer must not be able to present automated results as manual testing simply by not reading the prose.
  • limitations — what the report does not establish, as data, for the same reason.

reportVersion bumps when the shape changes incompatibly. Adding a field does not bump it, so read defensively.

Deprecation

Nothing that works stops working inside a major version, and nothing is removed without having been deprecated in a release you could have seen: marked @deprecated with its replacement named, left working for at least one further minor and 90 days, then removed in a major.

| | | | --------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | Patch | Behaviour unchanged, or a fix to behaviour that was already wrong. Never a signature change | | Minor | Additions. New exports, new optional fields, new accepted input. Adding a field to a returned object is a minor, so read defensively | | Major | Anything that could break a build or change an answer — a removed or renamed export, a narrowed input, a changed default |

Making an optional field required, or a required field optional, is a breaking change. Anything not reachable from this package's entry point is internal, whatever it looks like; reaching into dist/ is not covered.

fingerprint_version never changes without a migration. That is what the version is for: a change obliges a per-org re-fingerprint that preserves history — firstSeen is never reset — rather than a silent recomputation that orphans every issue.

A published version is immutable. A mistake is corrected by publishing another one, not by changing or unpublishing that one.

If the only correct fix for a vulnerability is a breaking change, we make it and say so. A policy that would rather leave a hole open than break a signature is not one worth having.