@secureport/core 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Andrew Jordan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # @secureport/core
2
+
3
+ The shared domain model for [Secureport](https://github.com/agbjordan/secureport).
4
+ The hosted API imports this package the same way a third party would — one
5
+ model, one implementation.
6
+
7
+ **MIT licensed. Zero runtime dependencies.**
8
+
9
+ ## Status: early scaffold
10
+
11
+ `0.2.x` is a deliberately small first release, published to prove the release
12
+ path rather than to be built against. **The domain shapes below are being
13
+ replaced wholesale in `0.3.0`** with the full model — `Finding`, `Issue`,
14
+ `Run`, `RunSummary`, `Snapshot` and `Coverage`, content-addressed fingerprints,
15
+ a CVSS-derived severity model, and a `reconcile()` that matches findings to
16
+ issues by fingerprint rather than by title.
17
+
18
+ If you are evaluating Secureport, wait for `0.3.0`. If you depend on `0.2.x`
19
+ anyway, pin it exactly.
20
+
21
+ ## Install
22
+
23
+ ```sh
24
+ npm install @secureport/core
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ```ts
30
+ import { reconcile, type Finding, type Issue } from '@secureport/core';
31
+
32
+ const open: Issue[] = [
33
+ { id: 'ISS-1', title: 'Missing HSTS header', severity: 'medium', status: 'open' },
34
+ ];
35
+
36
+ const findings: Finding[] = [
37
+ { title: 'Missing HSTS header', severity: 'medium' },
38
+ { title: 'Directory listing enabled', severity: 'low' },
39
+ ];
40
+
41
+ let n = 2;
42
+ const issues = reconcile(findings, open, () => `ISS-${n++}`);
43
+ // ISS-1 is reused for the HSTS finding; ISS-2 opens for the directory listing.
44
+ ```
45
+
46
+ ## The model in one paragraph
47
+
48
+ The tracked entity is the **issue**, not the finding. A **run** is one
49
+ execution against a target. A **finding** is one detection within one run and
50
+ is immutable — it never carries a status. Findings reconcile into **issues**,
51
+ which persist across runs and carry status, severity and history. That
52
+ distinction is the whole point of the package: two scanners reporting the same
53
+ weakness produce one issue with two sources, not two rows.
54
+
55
+ ## Contracts
56
+
57
+ Every export is a semver contract. From `0.3.0`, `fingerprint_version` and the
58
+ report JSON schema are versioned public contracts in their own right, and a
59
+ deprecation policy lands before `1.0.0`.
@@ -0,0 +1,3 @@
1
+ export * from './issue.js';
2
+ export * from './reconcile.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './issue.js';
2
+ export * from './reconcile.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,16 @@
1
+ /** Severity of an {@link Issue}, ordered from most to least urgent by convention. */
2
+ export type Severity = 'critical' | 'high' | 'medium' | 'low' | 'info';
3
+ /** Lifecycle state of an {@link Issue}. */
4
+ export type IssueStatus = 'open' | 'triaging' | 'accepted' | 'resolved';
5
+ /**
6
+ * A tracked issue — the entity Secureport reports on. One or more findings
7
+ * from one or more scanners can be merged into a single issue; the issue,
8
+ * not the finding, is what carries status and severity through review.
9
+ */
10
+ export interface Issue {
11
+ id: string;
12
+ title: string;
13
+ severity: Severity;
14
+ status: IssueStatus;
15
+ }
16
+ //# sourceMappingURL=issue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue.d.ts","sourceRoot":"","sources":["../src/issue.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;AAEvE,2CAA2C;AAC3C,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,CAAC;AAExE;;;;GAIG;AACH,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,WAAW,CAAC;CACrB"}
package/dist/issue.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=issue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue.js","sourceRoot":"","sources":["../src/issue.ts"],"names":[],"mappings":""}
@@ -0,0 +1,15 @@
1
+ import type { Issue } from './issue.js';
2
+ /** A raw scanner result, not yet folded into the tracked issue set. */
3
+ export interface Finding {
4
+ title: string;
5
+ severity: Issue['severity'];
6
+ }
7
+ /**
8
+ * Merges incoming findings into the tracked issue set. A finding whose title
9
+ * matches an open issue is folded into it; anything new opens an issue.
10
+ *
11
+ * Pure: no I/O, no clock, no randomness. Callers supply the id for any issue
12
+ * this opens via `nextId`.
13
+ */
14
+ export declare function reconcile(findings: Finding[], openIssues: Issue[], nextId: () => string): Issue[];
15
+ //# sourceMappingURL=reconcile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../src/reconcile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,uEAAuE;AACvE,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,MAAM,GAAG,KAAK,EAAE,CAgBjG"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Merges incoming findings into the tracked issue set. A finding whose title
3
+ * matches an open issue is folded into it; anything new opens an issue.
4
+ *
5
+ * Pure: no I/O, no clock, no randomness. Callers supply the id for any issue
6
+ * this opens via `nextId`.
7
+ */
8
+ export function reconcile(findings, openIssues, nextId) {
9
+ const byTitle = new Map(openIssues.map((issue) => [issue.title, issue]));
10
+ for (const finding of findings) {
11
+ const existing = byTitle.get(finding.title);
12
+ if (!existing) {
13
+ byTitle.set(finding.title, {
14
+ id: nextId(),
15
+ title: finding.title,
16
+ severity: finding.severity,
17
+ status: 'open',
18
+ });
19
+ }
20
+ }
21
+ return [...byTitle.values()];
22
+ }
23
+ //# sourceMappingURL=reconcile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../src/reconcile.ts"],"names":[],"mappings":"AAQA;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,QAAmB,EAAE,UAAmB,EAAE,MAAoB;IACtF,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IAEzE,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC5C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE;gBACzB,EAAE,EAAE,MAAM,EAAE;gBACZ,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,MAAM,EAAE,MAAM;aACf,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AAC/B,CAAC"}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@secureport/core",
3
+ "version": "0.2.0",
4
+ "description": "Shared domain model for Secureport. Imported by the hosted API the same way a third party would.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "src",
17
+ "LICENSE"
18
+ ],
19
+ "devDependencies": {
20
+ "typescript": "^5.7.2"
21
+ },
22
+ "license": "MIT",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/agbjordan/secureport.git",
26
+ "directory": "packages/core"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "scripts": {
32
+ "build": "tsc -p tsconfig.json",
33
+ "typecheck": "tsc --noEmit"
34
+ }
35
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './issue.js';
2
+ export * from './reconcile.js';
package/src/issue.ts ADDED
@@ -0,0 +1,17 @@
1
+ /** Severity of an {@link Issue}, ordered from most to least urgent by convention. */
2
+ export type Severity = 'critical' | 'high' | 'medium' | 'low' | 'info';
3
+
4
+ /** Lifecycle state of an {@link Issue}. */
5
+ export type IssueStatus = 'open' | 'triaging' | 'accepted' | 'resolved';
6
+
7
+ /**
8
+ * A tracked issue — the entity Secureport reports on. One or more findings
9
+ * from one or more scanners can be merged into a single issue; the issue,
10
+ * not the finding, is what carries status and severity through review.
11
+ */
12
+ export interface Issue {
13
+ id: string;
14
+ title: string;
15
+ severity: Severity;
16
+ status: IssueStatus;
17
+ }
@@ -0,0 +1,32 @@
1
+ import type { Issue } from './issue.js';
2
+
3
+ /** A raw scanner result, not yet folded into the tracked issue set. */
4
+ export interface Finding {
5
+ title: string;
6
+ severity: Issue['severity'];
7
+ }
8
+
9
+ /**
10
+ * Merges incoming findings into the tracked issue set. A finding whose title
11
+ * matches an open issue is folded into it; anything new opens an issue.
12
+ *
13
+ * Pure: no I/O, no clock, no randomness. Callers supply the id for any issue
14
+ * this opens via `nextId`.
15
+ */
16
+ export function reconcile(findings: Finding[], openIssues: Issue[], nextId: () => string): Issue[] {
17
+ const byTitle = new Map(openIssues.map((issue) => [issue.title, issue]));
18
+
19
+ for (const finding of findings) {
20
+ const existing = byTitle.get(finding.title);
21
+ if (!existing) {
22
+ byTitle.set(finding.title, {
23
+ id: nextId(),
24
+ title: finding.title,
25
+ severity: finding.severity,
26
+ status: 'open',
27
+ });
28
+ }
29
+ }
30
+
31
+ return [...byTitle.values()];
32
+ }