npq performs 14 different security and quality checks ("marshalls") on packages before installation. Each marshall targets a specific risk category and can be individually disabled via environment variables.
Detects newly published packages that may not have been vetted by the community.
/**
* Marshall: age
* Category: PackageHealth
* Title: "Checking package maturity"
*
* Checks:
* - Package creation age < 22 days (Error)
* - Specific version release > 365 days ago (Warning - unmaintained)
*
* Environment Variable: MARSHALL_DISABLE_AGE
*/
interface AgeMarshall extends BaseMarshall {
name: 'age';
categoryId: 'PackageHealth';
validate(pkg: PackageMetadata): Promise<{
packageAge: number; // Days since package creation
versionAge: number; // Days since version release
packageCreated: Date; // Package creation date
versionPublished: Date; // Version publish date
}>;
}Thresholds:
PACKAGE_AGE_THRESHOLD: 22 days (Error if package created more recently)PACKAGE_AGE_UNMAINTAINED_RISK: 365 days (Warning if version is older)Behavior:
Example Return Value:
// Success case
{
packageAge: 120,
versionAge: 30,
packageCreated: new Date('2023-10-01'),
versionPublished: new Date('2024-11-01')
}
// Error case (package too new)
{
packageAge: 15,
versionAge: 15,
packageCreated: new Date('2024-12-01'),
versionPublished: new Date('2024-12-01')
}
// Throws Error: "Detected a newly published package: created 15 days. Act carefully"
// Warning case (version too old)
{
packageAge: 400,
versionAge: 400,
packageCreated: new Date('2022-01-01'),
versionPublished: new Date('2022-01-01')
}
// Throws Warning: "Detected an old package: created 400 days ago"Example Output:
Error: Detected a newly published package: created < 22 days. Act carefully
Warning: Detected an old package: created 2 years agoEdge Cases:
time metadata: Falls back to version-specific timeDisable:
MARSHALL_DISABLE_AGE=1 npq install new-packageValidates package author reputation and publication history.
/**
* Marshall: author
* Category: SupplyChainSecurity
* Title: "Checking package author reputation"
*
* Checks:
* - First publish by author < 21 days (Error - new author risk)
* - Version published < 7 days (Error)
* - Version published 7-30 days (Warning)
* - Version published 30-45 days (Info check, no warning)
*
* Environment Variable: MARSHALL_DISABLE_AUTHOR
*/
interface AuthorMarshall extends BaseMarshall {
name: 'author';
categoryId: 'SupplyChainSecurity';
validate(pkg: PackageMetadata): Promise<{
authors: Array<{
email: string; // Author email
name: string; // Author name
firstPublishDate: Date; // First package publish date
daysSinceFirstPublish: number;
}>;
versionPublishDate: Date;
daysSinceVersionPublish: number;
}>;
}Features:
maintainers array and _npmUser fieldExample Return Value:
// Success case
{
authors: [
{
email: 'maintainer@example.com',
name: 'John Doe',
firstPublishDate: new Date('2020-01-01'),
daysSinceFirstPublish: 1800
}
],
versionPublishDate: new Date('2024-10-01'),
daysSinceVersionPublish: 60
}
// Error case (new author)
{
authors: [
{
email: 'newuser@example.com',
name: 'New User',
firstPublishDate: new Date('2024-11-15'),
daysSinceFirstPublish: 15
}
],
versionPublishDate: new Date('2024-11-15'),
daysSinceVersionPublish: 15
}
// Throws Error: "New author detected - first publish by newuser@example.com was 15 days ago"
// Error case (version too new)
{
authors: [...],
versionPublishDate: new Date('2024-12-20'),
daysSinceVersionPublish: 3
}
// Throws Error: "Version published 3 days ago"
// Warning case
{
authors: [...],
versionPublishDate: new Date('2024-11-20'),
daysSinceVersionPublish: 12
}
// Throws Warning: "Version published 12 days ago"Example Output:
Error: New author detected - first publish by user@example.com was 15 days ago
Warning: Version published 12 days agoEdge Cases:
_npmUser field as fallbackDisable:
MARSHALL_DISABLE_AUTHOR=1 npq install packageDetects deprecated packages and archived GitHub repositories.
/**
* Marshall: deprecation
* Category: PackageHealth
* Title: "Checking for deprecated packages"
*
* Checks:
* - Package version marked as deprecated in npm registry (Error)
* - GitHub repository marked as archived (Error)
*
* Environment Variable: MARSHALL_DISABLE_DEPRECATION
*/
interface DeprecationMarshall extends BaseMarshall {
name: 'deprecation';
categoryId: 'PackageHealth';
validate(pkg: PackageMetadata): Promise<{
isDeprecated: boolean; // Whether version is deprecated
deprecationMessage?: string; // Deprecation message from registry
repoArchived?: boolean; // Whether GitHub repo is archived
repoUrl?: string; // Repository URL
}>;
}Features:
GITHUB_TOKEN environment variable for higher rate limits (5000 req/hour vs 60)Example Return Value:
// Success case
{
isDeprecated: false,
repoArchived: false,
repoUrl: 'https://github.com/owner/repo'
}
// Error case (deprecated)
{
isDeprecated: true,
deprecationMessage: 'Please use v3.x instead',
repoUrl: 'https://github.com/owner/repo'
}
// Throws Error: "Package version 2.0.0 is deprecated: Please use v3.x instead"
// Error case (archived)
{
isDeprecated: false,
repoArchived: true,
repoUrl: 'https://github.com/owner/repo'
}
// Throws Error: "GitHub repository is archived and no longer maintained"
// Both deprecated and archived
{
isDeprecated: true,
deprecationMessage: 'Package deprecated',
repoArchived: true,
repoUrl: 'https://github.com/owner/repo'
}
// Throws both errorsExample Output:
Error: Package version 2.0.0 is deprecated: Please use v3.x instead
Error: GitHub repository is archived and no longer maintainedEdge Cases:
GitHub Token Setup:
export GITHUB_TOKEN=ghp_your_token_hereDisable:
MARSHALL_DISABLE_DEPRECATION=1 npq install old-packageValidates package popularity based on download statistics.
/**
* Marshall: downloads
* Category: PackageHealth
* Title: "Checking package popularity"
*
* Checks:
* - Downloads < 100/month (Error - unpopular package)
* - Downloads 100-10000/month (Warning - low popularity)
*
* Environment Variable: MARSHALL_DISABLE_DOWNLOADS
*/
interface DownloadsMarshall extends BaseMarshall {
name: 'downloads';
categoryId: 'PackageHealth';
validate(pkg: PackageMetadata): Promise<{
downloads: number; // Downloads in last month
threshold: number; // Error threshold (100)
upperThreshold: number; // Warning threshold (10000)
}>;
}Thresholds:
DOWNLOAD_COUNT_THRESHOLD: 100 downloads/month (Error)DOWNLOAD_COUNT_UPPER_THRESHOLD: 10,000 downloads/month (Warning)Data Source: npm registry API (https://api.npmjs.org/downloads/point/last-month/{package})
Example Return Value:
// Success case
{
downloads: 50000,
threshold: 100,
upperThreshold: 10000
}
// Error case (too few downloads)
{
downloads: 45,
threshold: 100,
upperThreshold: 10000
}
// Throws Error: "Package has only 45 downloads in the last month"
// Warning case (low popularity)
{
downloads: 2500,
threshold: 100,
upperThreshold: 10000
}
// Throws Warning: "Package has 2,500 downloads in the last month (low popularity)"Example Output:
Error: Package has only 45 downloads in the last month
Warning: Package has 2,500 downloads in the last month (low popularity)Edge Cases:
Disable:
MARSHALL_DISABLE_DOWNLOADS=1 npq install unpopular-packageDetects maintainer email addresses with expired or invalid domains.
/**
* Marshall: maintainers_expired_emails
* Category: PackageHealth
* Title: "Checking for expired maintainer domains"
*
* Checks:
* - Maintainer email domain expired/invalid (Error - account takeover risk)
*
* Environment Variable: MARSHALL_DISABLE_MAINTAINERS_EXPIRED_EMAILS
*/
interface ExpiredDomainsMarshall extends BaseMarshall {
name: 'maintainers_expired_emails';
categoryId: 'PackageHealth';
validate(pkg: PackageMetadata): Promise<{
maintainers: Array<{
email: string; // Maintainer email
domain: string; // Email domain
hasValidNS: boolean; // Whether domain has valid NS records
expired: boolean; // Whether domain appears expired
}>;
}>;
}Features:
Security Impact: Expired domains can be re-registered by attackers to gain control of npm accounts via password reset emails (Account Takeover attacks).
Example Return Value:
// Success case
{
maintainers: [
{
email: 'maintainer@example.com',
domain: 'example.com',
hasValidNS: true,
expired: false
}
]
}
// Error case (expired domain)
{
maintainers: [
{
email: 'user@expireddomain.com',
domain: 'expireddomain.com',
hasValidNS: false,
expired: true
}
]
}
// Throws Error: "Maintainer email domain expired: user@expireddomain.com"
// Multiple maintainers
{
maintainers: [
{ email: 'user1@valid.com', domain: 'valid.com', hasValidNS: true, expired: false },
{ email: 'user2@expired.com', domain: 'expired.com', hasValidNS: false, expired: true }
]
}
// Throws Error for expired domainExample Output:
Error: Maintainer email domain expired: user@expireddomain.comEdge Cases:
Disable:
MARSHALL_DISABLE_MAINTAINERS_EXPIRED_EMAILS=1 npq install packageValidates presence of package license information.
/**
* Marshall: license
* Category: SupplyChainSecurity
* Title: "Checking for package license"
*
* Checks:
* - LICENSE field missing or empty (Error)
* - Security placeholder package detected (Error)
*
* Environment Variable: MARSHALL_DISABLE_LICENSE
*/
interface LicenseMarshall extends BaseMarshall {
name: 'license';
categoryId: 'SupplyChainSecurity';
validate(pkg: PackageMetadata): Promise<{
license: string | null; // License string or null
hasLicense: boolean; // Whether license field exists
isPlaceholder: boolean; // Whether package is security placeholder
}>;
}Features:
Example Return Value:
// Success case
{
license: 'MIT',
hasLicense: true,
isPlaceholder: false
}
// Error case (no license)
{
license: null,
hasLicense: false,
isPlaceholder: false
}
// Throws Error: "Package has no license field"
// Error case (placeholder)
{
license: null,
hasLicense: false,
isPlaceholder: true
}
// Throws Error: "Package is a security placeholder package"
// License as object (SPDX format)
{
license: { type: 'MIT', url: 'https://...' },
hasLicense: true,
isPlaceholder: false
}Example Output:
Error: Package has no license field
Error: Package is a security placeholder packageEdge Cases:
Disable:
MARSHALL_DISABLE_LICENSE=1 npq install unlicensed-packageDetects newly introduced command-line executables between versions.
/**
* Marshall: newBin
* Category: SupplyChainSecurity
* Title: "Checking for newly introduced binaries"
*
* Checks:
* - New binary executables introduced in this version (Warning)
*
* Environment Variable: MARSHALL_DISABLE_NEWBIN
*/
interface NewBinMarshall extends BaseMarshall {
name: 'newBin';
categoryId: 'SupplyChainSecurity';
validate(pkg: PackageMetadata): Promise<{
currentBinaries: Record<string, string>; // Current version binaries
previousBinaries: Record<string, string>; // Previous version binaries
newBinaries: Array<{ // Newly introduced binaries
name: string; // Binary name
path: string; // Binary path
}>;
}>;
}Features:
bin field between current and previous versionnode_modules/.bin/Bin Format Normalization:
// String format
"bin": "./bin/cli.js"
// Normalized to:
{ "package-name": "./bin/cli.js" }
// Object format
"bin": {
"cmd1": "./bin/cmd1.js",
"cmd2": "./bin/cmd2.js"
}Example Return Value:
// Success case (no new binaries)
{
currentBinaries: { 'package-name': './bin/cli.js' },
previousBinaries: { 'package-name': './bin/cli.js' },
newBinaries: []
}
// Warning case (new binary)
{
currentBinaries: {
'package-name': './bin/cli.js',
'malicious-cmd': './bin/cmd.js'
},
previousBinaries: {
'package-name': './bin/cli.js'
},
newBinaries: [
{ name: 'malicious-cmd', path: './bin/cmd.js' }
]
}
// Throws Warning: "New binary introduced: malicious-cmd (./bin/cmd.js)"
// First version (no previous binaries)
{
currentBinaries: { 'package-name': './bin/cli.js' },
previousBinaries: {},
newBinaries: [
{ name: 'package-name', path: './bin/cli.js' }
]
}
// Throws Warning for all binaries in first versionExample Output:
Warning: New binary introduced: malicious-cmd (./bin/cmd.js)Edge Cases:
Disable:
MARSHALL_DISABLE_NEWBIN=1 npq install packageVerifies package provenance and attestations using cryptographic signatures.
/**
* Marshall: provenance
* Category: SupplyChainSecurity
* Title: "Checking package provenance"
*
* Checks:
* - Package provenance/attestations present and valid (Warning if missing)
*
* Environment Variable: MARSHALL_DISABLE_PROVENANCE
*/
interface ProvenanceMarshall extends BaseMarshall {
name: 'provenance';
categoryId: 'SupplyChainSecurity';
validate(pkg: PackageMetadata): Promise<{
hasProvenance: boolean; // Whether provenance attestations exist
isValid: boolean; // Whether attestations are valid
attestations?: Array<{ // Attestation details
type: string; // Attestation type
verified: boolean; // Whether verification succeeded
}>;
}>;
}Features:
https://registry.npmjs.org/-/npm/v1/keysProvenance Attestations: Cryptographic proof of package build and publish process, linking package contents to source code and build environment.
Example Return Value:
// Success case (has valid provenance)
{
hasProvenance: true,
isValid: true,
attestations: [
{ type: 'provenance', verified: true }
]
}
// Warning case (no provenance)
{
hasProvenance: false,
isValid: false
}
// Throws Warning: "Package has no provenance attestations"
// Warning case (invalid provenance)
{
hasProvenance: true,
isValid: false,
attestations: [
{ type: 'provenance', verified: false }
]
}
// Throws Warning: "Package provenance attestations are invalid"Example Output:
Warning: Package has no provenance attestationsEdge Cases:
Disable:
MARSHALL_DISABLE_PROVENANCE=1 npq install packageValidates that package repository and homepage URLs are accessible.
/**
* Marshall: repo
* Category: PackageHealth
* Title: "Checking repository availability"
*
* Checks:
* - Repository URL accessible (Warning if not)
* - Homepage accessible as fallback (Warning if not)
* - Either repo or homepage exists (Error if neither)
*
* Environment Variable: MARSHALL_DISABLE_REPO
*/
interface RepoMarshall extends BaseMarshall {
name: 'repo';
categoryId: 'PackageHealth';
validate(pkg: PackageMetadata): Promise<{
repoUrl?: string; // Repository URL
homepageUrl?: string; // Homepage URL
repoAccessible: boolean; // Whether repo URL is accessible
homepageAccessible: boolean; // Whether homepage URL is accessible
hasAnyUrl: boolean; // Whether at least one URL exists
}>;
}Features:
URL Sources:
repository field in package.jsonhomepage field in package.jsonExample Return Value:
// Success case
{
repoUrl: 'https://github.com/owner/repo',
homepageUrl: 'https://example.com',
repoAccessible: true,
homepageAccessible: true,
hasAnyUrl: true
}
// Warning case (repo not accessible)
{
repoUrl: 'https://github.com/owner/repo',
homepageUrl: 'https://example.com',
repoAccessible: false,
homepageAccessible: true,
hasAnyUrl: true
}
// Throws Warning: "Repository URL is not accessible"
// Error case (no accessible URLs)
{
repoUrl: 'https://github.com/owner/repo',
homepageUrl: 'https://example.com',
repoAccessible: false,
homepageAccessible: false,
hasAnyUrl: true
}
// Throws Error: "Package has no accessible repository or homepage"
// Error case (no URLs at all)
{
repoAccessible: false,
homepageAccessible: false,
hasAnyUrl: false
}
// Throws Error: "Package has no accessible repository or homepage"Example Output:
Warning: Repository URL is not accessible
Warning: Homepage URL is not accessible
Error: Package has no accessible repository or homepageEdge Cases:
Disable:
MARSHALL_DISABLE_REPO=1 npq install packageDetects potentially malicious pre/post install scripts.
/**
* Marshall: scripts
* Category: MalwareDetection
* Title: "Checking for install scripts"
*
* Checks:
* - Pre/post install scripts present (Error - malware risk)
*
* Environment Variable: MARSHALL_DISABLE_SCRIPTS
*/
interface ScriptsMarshall extends BaseMarshall {
name: 'scripts';
categoryId: 'MalwareDetection';
validate(pkg: PackageMetadata): Promise<{
hasInstallScripts: boolean; // Whether install scripts exist
scripts: Array<{ // Detected scripts
name: string; // Script name (preinstall, install, postinstall)
command: string; // Script command
}>;
}>;
}Blacklisted Scripts:
installpreinstallpostinstallSecurity Risk: Install scripts execute arbitrary code during package installation, which can be used for:
Example Return Value:
// Success case (no install scripts)
{
hasInstallScripts: false,
scripts: []
}
// Error case (preinstall)
{
hasInstallScripts: true,
scripts: [
{ name: 'preinstall', command: 'node setup.js' }
]
}
// Throws Error: "Package has preinstall script (potential malware)"
// Error case (postinstall)
{
hasInstallScripts: true,
scripts: [
{ name: 'postinstall', command: 'node postinstall.js' }
]
}
// Throws Error: "Package has postinstall script (potential malware)"
// Error case (both)
{
hasInstallScripts: true,
scripts: [
{ name: 'preinstall', command: 'node setup.js' },
{ name: 'postinstall', command: 'node postinstall.js' }
]
}
// Throws Error for each scriptExample Output:
Error: Package has preinstall script (potential malware)
Error: Package has postinstall script (potential malware)Edge Cases:
Disable:
MARSHALL_DISABLE_SCRIPTS=1 npq install package-with-scriptsVerifies npm registry cryptographic signatures on packages.
/**
* Marshall: signatures
* Category: SupplyChainSecurity
* Title: "Checking package signatures"
*
* Checks:
* - Package signature valid (Error if invalid)
* - Registry key expired (Warning)
* - Registry key missing (Error)
*
* Environment Variable: MARSHALL_DISABLE_SIGNATURES
*/
interface SignaturesMarshall extends BaseMarshall {
name: 'signatures';
categoryId: 'SupplyChainSecurity';
validate(pkg: PackageMetadata): Promise<{
hasSignature: boolean; // Whether package has signature
isValid: boolean; // Whether signature is valid
keyExpired?: boolean; // Whether registry key is expired
keyMissing?: boolean; // Whether key is missing
keyId?: string; // Signature key ID
}>;
}Features:
registryKeysCache singletonVerification Process:
Example Return Value:
// Success case
{
hasSignature: true,
isValid: true,
keyId: 'key-123'
}
// Error case (invalid signature)
{
hasSignature: true,
isValid: false,
keyId: 'key-123'
}
// Throws Error: "Package has invalid registry signature"
// Warning case (expired key)
{
hasSignature: true,
isValid: true,
keyExpired: true,
keyId: 'key-123'
}
// Throws Warning: "Registry key for signature has expired"
// Error case (missing key)
{
hasSignature: true,
isValid: false,
keyMissing: true,
keyId: 'key-123'
}
// Throws Error: "No corresponding public key found for signature"
// No signature
{
hasSignature: false,
isValid: false
}
// No error or warning (signatures optional)Example Output:
Error: Package has invalid registry signature
Warning: Registry key for signature has expired
Error: No corresponding public key found for signatureEdge Cases:
Disable:
MARSHALL_DISABLE_SIGNATURES=1 npq install packageChecks for known security vulnerabilities using Snyk or OSV databases.
/**
* Marshall: snyk
* Category: SupplyChainSecurity
* Title: "Checking for known vulnerabilities"
*
* Checks:
* - Known vulnerabilities in package (Error if count > 0)
* - Malicious package detection (Error)
*
* Environment Variable: MARSHALL_DISABLE_SNYK
*/
interface SnykMarshall extends BaseMarshall {
name: 'snyk';
categoryId: 'SupplyChainSecurity';
validate(pkg: PackageMetadata): Promise<{
vulnerabilityCount: number; // Number of vulnerabilities found
isMalicious: boolean; // Whether package is marked as malicious
vulnerabilities?: Array<{ // Vulnerability details
id: string; // Vulnerability ID
title: string; // Vulnerability title
severity: string; // Severity level
url: string; // Vulnerability URL
}>;
source: 'snyk' | 'osv'; // Data source used
}>;
}Data Sources:
Snyk API (Primary, if token available):
SNYK_TOKEN or SNYK_API_TOKEN environment variable~/.config/configstore/snyk.jsonhttps://snyk.io/api/v1/vuln/npm/{package}@{version}OSV API (Fallback, if no Snyk token):
https://api.osv.dev/v1/queryMalicious Package Detection:
vulnerability.title === 'Malicious Package'vuln.database_specific['malicious-packages-origins']Environment Variables:
SNYK_TOKEN or SNYK_API_TOKEN: API authenticationSNYK_API_URL or SNYK_API: Custom API endpointExample Return Value:
// Success case (no vulnerabilities)
{
vulnerabilityCount: 0,
isMalicious: false,
source: 'snyk'
}
// Error case (vulnerabilities)
{
vulnerabilityCount: 3,
isMalicious: false,
vulnerabilities: [
{ id: 'SNYK-123', title: 'SQL Injection', severity: 'high', url: 'https://...' },
{ id: 'SNYK-456', title: 'XSS', severity: 'medium', url: 'https://...' },
{ id: 'SNYK-789', title: 'DoS', severity: 'low', url: 'https://...' }
],
source: 'snyk'
}
// Throws Error: "3 vulnerable path(s) found: https://snyk.io/vuln/npm:package-name"
// Error case (malicious)
{
vulnerabilityCount: 1,
isMalicious: true,
vulnerabilities: [
{ id: 'SNYK-MAL', title: 'Malicious Package', severity: 'critical', url: 'https://...' }
],
source: 'snyk'
}
// Throws Error: "Malicious package found: https://snyk.io/vuln/npm:package-name"
// OSV fallback
{
vulnerabilityCount: 5,
isMalicious: false,
source: 'osv'
}
// Throws Error: "5 vulnerabilities found by OSV for package-name"Example Output:
Error: Malicious package found: https://snyk.io/vuln/npm:package-name
Error: 3 vulnerable path(s) found: https://snyk.io/vuln/npm:package-name
Error: 5 vulnerabilities found by OSV for package-nameEdge Cases:
Snyk Setup:
# Option 1: Environment variable
export SNYK_TOKEN=your-snyk-token
# Option 2: Snyk CLI (creates config file)
npm install -g snyk
snyk authDisable:
MARSHALL_DISABLE_SNYK=1 npq install packageDetects packages with names similar to popular packages.
/**
* Marshall: typosquatting
* Category: PackageHealth
* Title: "Checking for typosquatting"
*
* Checks:
* - Package name similar to popular package (Error if distance < 3)
*
* Environment Variable: MARSHALL_DISABLE_TYPOSQUATTING
*/
interface TyposquattingMarshall extends BaseMarshall {
name: 'typosquatting';
categoryId: 'PackageHealth';
validate(pkg: PackageMetadata): Promise<{
similarPackages: Array<{ // Packages with similar names
name: string; // Popular package name
distance: number; // Levenshtein distance
}>;
minDistance: number; // Minimum distance to any popular package
}>;
}Features:
data/top-packages.json['npq', 'ai', 'bun', 'deno'] (exempted from checks)Typosquatting Examples:
expresss → express (distance: 1)loadash → lodash (distance: 1)reqeust → request (distance: 2)Levenshtein Distance: Minimum number of single-character edits (insertions, deletions, substitutions) needed to change one string into another.
Example Return Value:
// Success case (not similar)
{
similarPackages: [],
minDistance: 5
}
// Error case (typosquatting)
{
similarPackages: [
{ name: 'express', distance: 1 }
],
minDistance: 1
}
// Throws Error: "Package name is similar to popular package \"express\" (possible typosquatting)"
// Multiple similar packages
{
similarPackages: [
{ name: 'express', distance: 1 },
{ name: 'lodash', distance: 2 }
],
minDistance: 1
}
// Throws Error for closest matchExample Output:
Error: Package name is similar to popular package "express" (possible typosquatting)Edge Cases:
Disable:
MARSHALL_DISABLE_TYPOSQUATTING=1 npq install expresssDetects recently published versions that may not have been vetted.
/**
* Marshall: version_maturity
* Category: SupplyChainSecurity
* Title: "Checking version maturity"
*
* Checks:
* - Version published < 7 days ago (Error)
*
* Environment Variable: MARSHALL_DISABLE_VERSION_MATURITY
*/
interface VersionMaturityMarshall extends BaseMarshall {
name: 'version_maturity';
categoryId: 'SupplyChainSecurity';
validate(pkg: PackageMetadata): Promise<{
versionPublishDate: Date; // When version was published
daysSincePublish: number; // Days since publish
isRecent: boolean; // Whether version is < 7 days old
}>;
}Threshold: VERSION_AGE_THRESHOLD: 7 days
Features:
Difference from Age Marshall:
Example Return Value:
// Success case
{
versionPublishDate: new Date('2024-10-01'),
daysSincePublish: 60,
isRecent: false
}
// Error case (too recent)
{
versionPublishDate: new Date('2024-12-20'),
daysSincePublish: 3,
isRecent: true
}
// Throws Error: "Version published 3 days ago (too recent)"
// Edge case (exactly 7 days)
{
versionPublishDate: new Date('2024-12-13'),
daysSincePublish: 7,
isRecent: false
}
// No error (threshold is < 7 days)Example Output:
Error: Version published 3 days ago (too recent)Edge Cases:
Disable:
MARSHALL_DISABLE_VERSION_MATURITY=1 npq install package@latest| Marshall | Category | Error Conditions | Warning Conditions | Env Variable |
|---|---|---|---|---|
| age | PackageHealth | Package < 22 days old | Version > 365 days old | MARSHALL_DISABLE_AGE |
| author | SupplyChainSecurity | First publish by author < 21 days, Version < 7 days | Version 7-30 days old | MARSHALL_DISABLE_AUTHOR |
| deprecation | PackageHealth | Deprecated, Repo archived | - | MARSHALL_DISABLE_DEPRECATION |
| downloads | PackageHealth | < 100/month | 100-10000/month | MARSHALL_DISABLE_DOWNLOADS |
| expiredDomains | PackageHealth | Maintainer domain expired | - | MARSHALL_DISABLE_MAINTAINERS_EXPIRED_EMAILS |
| license | SupplyChainSecurity | No license field | - | MARSHALL_DISABLE_LICENSE |
| newBin | SupplyChainSecurity | - | New binary introduced | MARSHALL_DISABLE_NEWBIN |
| provenance | SupplyChainSecurity | - | No provenance attestations | MARSHALL_DISABLE_PROVENANCE |
| repo | PackageHealth | No accessible repo/homepage | Repo/homepage inaccessible | MARSHALL_DISABLE_REPO |
| scripts | MalwareDetection | Pre/post install scripts | - | MARSHALL_DISABLE_SCRIPTS |
| signatures | SupplyChainSecurity | Invalid signature, Missing key | Expired key | MARSHALL_DISABLE_SIGNATURES |
| snyk | SupplyChainSecurity | Vulnerabilities found, Malicious package | - | MARSHALL_DISABLE_SNYK |
| typosquatting | PackageHealth | Name similar to popular package | - | MARSHALL_DISABLE_TYPOSQUATTING |
| version-maturity | SupplyChainSecurity | Version < 7 days old | - | MARSHALL_DISABLE_VERSION_MATURITY |
Errors (Fatal):
Warnings (Non-fatal):
Auto-Continue: When only warnings are detected, npq automatically proceeds after 15 seconds. Disable with --disable-auto-continue flag or NPQ_DISABLE_AUTO_CONTINUE=true environment variable.
# Disable checks for internal/corporate packages
export MARSHALL_DISABLE_DOWNLOADS=1
export MARSHALL_DISABLE_AGE=1
export MARSHALL_DISABLE_TYPOSQUATTING=1
npq install @company/internal-package# Disable all non-security checks
export MARSHALL_DISABLE_AGE=1
export MARSHALL_DISABLE_AUTHOR=1
export MARSHALL_DISABLE_DOWNLOADS=1
export MARSHALL_DISABLE_REPO=1
export MARSHALL_DISABLE_TYPOSQUATTING=1
export MARSHALL_DISABLE_DEPRECATION=1
# Keep security checks enabled:
# - snyk (vulnerabilities)
# - scripts (malware)
# - signatures (integrity)
# - provenance (supply chain)
# - expiredDomains (account takeover)
npq install package# Disable slow network-dependent checks
export MARSHALL_DISABLE_REPO=1 # Avoids HTTP requests
export MARSHALL_DISABLE_DEPRECATION=1 # Avoids GitHub API calls
export MARSHALL_DISABLE_PROVENANCE=1 # Avoids sigstore verification
npq install packageAll marshalls extend the BaseMarshall class and implement the validate(pkg) method:
/**
* Base class for all marshall implementations
*/
class BaseMarshall {
constructor(options: { packageRepoUtils: PackageRepoUtils });
// Marshall metadata
name: string; // Marshall identifier
categoryId: string; // Category ID
title(): string; // Human-readable title
// Initialize result structure
init(ctx: ExecutionContext): void;
// Run validation on all packages
run(ctx: ExecutionContext): Promise<MarshallResult[]>;
// Validate single package
checkPackage(pkg: PackageMetadata, ctx: ExecutionContext): Promise<any>;
// Check if marshall is enabled
isEnabled(): boolean;
// Add error or warning message
setMessage(msg: { pkg: string; message: string }, isWarning: boolean): void;
// Resolve version specs to actual versions
resolvePackageVersion(
packageName: string,
versionSpec: string,
packageData?: any
): Promise<string | null>;
// Abstract method to be implemented by subclasses
validate(pkg: PackageMetadata): Promise<any>;
}
/**
* Execution context passed to marshalls
*/
interface ExecutionContext {
pkgs: PackageMetadata[];
marshalls: {
[marshallName: string]: MarshallResult;
};
}To add a custom marshall:
lib/marshalls/custom.marshall.jsBaseMarshallvalidate(pkg) methodError for fatal issues, Warning for non-fatal// Example custom marshall
const BaseMarshall = require('./baseMarshall');
const Warning = require('../helpers/warning');
const { marshallCategories } = require('./constants');
const MARSHALL_NAME = 'custom';
class Marshall extends BaseMarshall {
constructor(options) {
super(options);
this.name = MARSHALL_NAME;
this.categoryId = marshallCategories.PackageHealth.id;
}
title() {
return 'Checking custom criteria';
}
async validate(pkg) {
// Fetch package data
const data = await this.packageRepoUtils.getPackageInfo(pkg.packageName);
// Perform checks
if (/* error condition */) {
throw new Error('Error message');
}
if (/* warning condition */) {
throw new Warning('Warning message');
}
// Return data on success
return data;
}
}
module.exports = Marshall;Marshall will be automatically discovered and executed.