From 09404ed19f5af96bde1e9c120df763dcc89148e7 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Sat, 20 Dec 2025 13:12:51 -0600 Subject: [PATCH 1/2] feat: add trust level computation core module --- src/core/index.ts | 6 ++ src/core/trust.ts | 160 ++++++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 3 + 3 files changed, 169 insertions(+) create mode 100644 src/core/index.ts create mode 100644 src/core/trust.ts diff --git a/src/core/index.ts b/src/core/index.ts new file mode 100644 index 0000000..0dca733 --- /dev/null +++ b/src/core/index.ts @@ -0,0 +1,6 @@ +/** + * Core modules containing pure, reusable logic that can be imported + * by external tools like the e18e GitHub Action. + */ +export * from './trust.js'; + diff --git a/src/core/trust.ts b/src/core/trust.ts new file mode 100644 index 0000000..ed4801f --- /dev/null +++ b/src/core/trust.ts @@ -0,0 +1,160 @@ +/** + * Trust level computation for npm packages based on provenance attestations. + * + * This module provides pure functions for computing trust levels without + * making network calls. The actual package metadata must be fetched separately. + */ + +/** + * The provenance status of a package, indicating its level of trust. + * + * - `trusted-with-provenance`: Published by a trusted publisher with provenance attestations + * - `provenance`: Has provenance attestations but not from a trusted publisher + * - `none`: No provenance attestations + */ +export type ProvenanceStatus = + | 'trusted-with-provenance' + | 'provenance' + | 'none'; + +/** + * Metadata about an npm package version, containing provenance information. + * This is a subset of the full npm registry metadata. + */ +export interface PackageProvenanceMetadata { + name: string; + version: string; + dist?: { + attestations?: { + url: string; + provenance?: unknown; + }; + }; + _npmUser?: { + name: string; + email: string; + trustedPublisher?: unknown; + }; +} + +/** + * Result of computing the minimum trust level across a set of packages. + */ +export interface MinTrustLevelResult { + level: number; + status: ProvenanceStatus; +} + +/** + * Summary of trust levels across a set of dependencies. + */ +export interface TrustSummary { + trusted: number; + provenance: number; + untrusted: number; + total: number; +} + +/** + * Determines the provenance status of a package from its metadata. + * + * @param meta - Package metadata from the npm registry + * @returns The provenance status of the package + */ +export function getProvenance(meta: PackageProvenanceMetadata): ProvenanceStatus { + if (meta._npmUser?.trustedPublisher) { + return 'trusted-with-provenance'; + } + if (meta.dist?.attestations?.provenance) { + return 'provenance'; + } + return 'none'; +} + +/** + * Converts a provenance status to a numeric trust level. + * + * Higher numbers indicate higher trust: + * - 2: trusted-with-provenance + * - 1: provenance + * - 0: none + * + * @param status - The provenance status + * @returns The numeric trust level (0-2) + */ +export function getTrustLevel(status: ProvenanceStatus): number { + switch (status) { + case 'trusted-with-provenance': + return 2; + case 'provenance': + return 1; + case 'none': + return 0; + default: + return 0; + } +} + +/** + * Finds the minimum trust level across a set of provenance statuses. + * + * This is useful for determining the overall trust level of a dependency tree, + * where the weakest link determines the overall security. + * + * @param statuses - An iterable of provenance statuses + * @returns The minimum trust level and its corresponding status + */ +export function getMinTrustLevel( + statuses: Iterable +): MinTrustLevelResult { + let result: MinTrustLevelResult | null = null; + + for (const status of statuses) { + const level = getTrustLevel(status); + if (result === null || level < result.level) { + result = {level, status}; + } + } + + if (!result) { + return {level: 0, status: 'none'}; + } + + return result; +} + +/** + * Computes a summary of trust levels from a collection of provenance statuses. + * + * @param statuses - An iterable of provenance statuses + * @returns A summary with counts for each trust category + */ +export function computeTrustSummary( + statuses: Iterable +): TrustSummary { + let trusted = 0; + let provenance = 0; + let untrusted = 0; + + for (const status of statuses) { + switch (status) { + case 'trusted-with-provenance': + trusted++; + break; + case 'provenance': + provenance++; + break; + case 'none': + untrusted++; + break; + } + } + + return { + trusted, + provenance, + untrusted, + total: trusted + provenance + untrusted + }; +} + diff --git a/src/index.ts b/src/index.ts index c5fa6fd..33171c5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,3 +4,6 @@ import type {PackageModuleType} from './compute-type.js'; export type {Message, Options, PackageModuleType, Stat}; export {report} from './analyze/report.js'; + +// Core modules - reusable logic for external tools +export * from './core/index.js'; From 974ca369fc2393659740e1bcf1230058b920822d Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Sat, 20 Dec 2025 13:15:20 -0600 Subject: [PATCH 2/2] format --- src/core/index.ts | 1 - src/core/trust.ts | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/index.ts b/src/core/index.ts index 0dca733..877cfe0 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -3,4 +3,3 @@ * by external tools like the e18e GitHub Action. */ export * from './trust.js'; - diff --git a/src/core/trust.ts b/src/core/trust.ts index ed4801f..197618c 100644 --- a/src/core/trust.ts +++ b/src/core/trust.ts @@ -61,7 +61,9 @@ export interface TrustSummary { * @param meta - Package metadata from the npm registry * @returns The provenance status of the package */ -export function getProvenance(meta: PackageProvenanceMetadata): ProvenanceStatus { +export function getProvenance( + meta: PackageProvenanceMetadata +): ProvenanceStatus { if (meta._npmUser?.trustedPublisher) { return 'trusted-with-provenance'; } @@ -157,4 +159,3 @@ export function computeTrustSummary( total: trusted + provenance + untrusted }; } -