Skip to content

Commit 12e2ffb

Browse files
authored
feat: add trust level computation core module (#138)
* feat: add trust level computation core module * format * chore(core): remove barrel file, drop PackageProvenanceMetadata type * format
1 parent d9a6585 commit 12e2ffb

File tree

2 files changed

+145
-0
lines changed

2 files changed

+145
-0
lines changed

src/core/trust.ts

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/**
2+
* Trust level computation for npm packages based on provenance attestations.
3+
*
4+
* This module provides pure functions for computing trust levels without
5+
* making network calls. The actual package metadata must be fetched separately.
6+
*/
7+
8+
/**
9+
* The provenance status of a package, indicating its level of trust.
10+
*
11+
* - `trusted-with-provenance`: Published by a trusted publisher with provenance attestations
12+
* - `provenance`: Has provenance attestations but not from a trusted publisher
13+
* - `none`: No provenance attestations
14+
*/
15+
export type ProvenanceStatus =
16+
| 'trusted-with-provenance'
17+
| 'provenance'
18+
| 'none';
19+
20+
/**
21+
* Result of computing the minimum trust level across a set of packages.
22+
*/
23+
export interface MinTrustLevelResult {
24+
level: number;
25+
status: ProvenanceStatus;
26+
}
27+
28+
/**
29+
* Summary of trust levels across a set of dependencies.
30+
*/
31+
export interface TrustSummary {
32+
trusted: number;
33+
provenance: number;
34+
untrusted: number;
35+
total: number;
36+
}
37+
38+
/**
39+
* Determines the provenance status of a package from its metadata.
40+
*
41+
* @param meta - Package metadata from the npm registry (must have `dist?.attestations?.provenance` and/or `_npmUser?.trustedPublisher`)
42+
* @returns The provenance status of the package
43+
*/
44+
export function getProvenance(meta: {
45+
dist?: {attestations?: {provenance?: unknown}};
46+
_npmUser?: {trustedPublisher?: unknown};
47+
}): ProvenanceStatus {
48+
if (meta._npmUser?.trustedPublisher) {
49+
return 'trusted-with-provenance';
50+
}
51+
if (meta.dist?.attestations?.provenance) {
52+
return 'provenance';
53+
}
54+
return 'none';
55+
}
56+
57+
/**
58+
* Converts a provenance status to a numeric trust level.
59+
*
60+
* Higher numbers indicate higher trust:
61+
* - 2: trusted-with-provenance
62+
* - 1: provenance
63+
* - 0: none
64+
*
65+
* @param status - The provenance status
66+
* @returns The numeric trust level (0-2)
67+
*/
68+
export function getTrustLevel(status: ProvenanceStatus): number {
69+
switch (status) {
70+
case 'trusted-with-provenance':
71+
return 2;
72+
case 'provenance':
73+
return 1;
74+
case 'none':
75+
return 0;
76+
default:
77+
return 0;
78+
}
79+
}
80+
81+
/**
82+
* Finds the minimum trust level across a set of provenance statuses.
83+
*
84+
* This is useful for determining the overall trust level of a dependency tree,
85+
* where the weakest link determines the overall security.
86+
*
87+
* @param statuses - An iterable of provenance statuses
88+
* @returns The minimum trust level and its corresponding status
89+
*/
90+
export function getMinTrustLevel(
91+
statuses: Iterable<ProvenanceStatus>
92+
): MinTrustLevelResult {
93+
let result: MinTrustLevelResult | null = null;
94+
95+
for (const status of statuses) {
96+
const level = getTrustLevel(status);
97+
if (result === null || level < result.level) {
98+
result = {level, status};
99+
}
100+
}
101+
102+
if (!result) {
103+
return {level: 0, status: 'none'};
104+
}
105+
106+
return result;
107+
}
108+
109+
/**
110+
* Computes a summary of trust levels from a collection of provenance statuses.
111+
*
112+
* @param statuses - An iterable of provenance statuses
113+
* @returns A summary with counts for each trust category
114+
*/
115+
export function computeTrustSummary(
116+
statuses: Iterable<ProvenanceStatus>
117+
): TrustSummary {
118+
let trusted = 0;
119+
let provenance = 0;
120+
let untrusted = 0;
121+
122+
for (const status of statuses) {
123+
switch (status) {
124+
case 'trusted-with-provenance':
125+
trusted++;
126+
break;
127+
case 'provenance':
128+
provenance++;
129+
break;
130+
case 'none':
131+
untrusted++;
132+
break;
133+
}
134+
}
135+
136+
return {
137+
trusted,
138+
provenance,
139+
untrusted,
140+
total: trusted + provenance + untrusted
141+
};
142+
}

src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ import type {PackageModuleType} from './compute-type.js';
44
export type {Message, Options, PackageModuleType, Stat};
55

66
export {report} from './analyze/report.js';
7+
8+
// Core modules - reusable logic for external tools
9+
export * from './core/trust.js';

0 commit comments

Comments
 (0)