The ValidateHost class validates that all package resources come from whitelisted hosts, preventing packages from unauthorized registries.
Creates a new host validator instance.
/**
* Creates a new host validator
* @param {Object} options - Validator configuration
* @param {Object} options.packages - Package object from parsed lockfile
* @param {Function} [options.debug] - Optional debug logging function
* @throws {Error} If packages is not provided or not an object
*/
class ValidateHost {
constructor(options: {
packages: Object;
debug?: Function;
});
}Requirements: The packages parameter must be an object (typically from ParseLockfile.parseSync().object). Throws an error if not provided or not an object.
Usage Examples:
const { ValidateHost, ParseLockfile } = require('lockfile-lint-api');
// Parse lockfile first
const parser = new ParseLockfile({ lockfilePath: './package-lock.json' });
const lockfile = parser.parseSync();
// Create validator
const validator = new ValidateHost({ packages: lockfile.object });
// With debug logging
const debug = require('debug')('my-app');
const validatorWithDebug = new ValidateHost({
packages: lockfile.object,
debug: debug
});Validates that all packages in the lockfile come from allowed hosts.
/**
* Validates all packages against allowed hosts
* @param {string[]} hosts - Array of allowed host strings (supports shortcuts: 'npm', 'yarn', 'verdaccio')
* @param {Object} [options] - Validation options
* @param {boolean} [options.emptyHostname] - If true, allows packages with empty hostnames
* @returns {Object} Validation result with type ('success' or 'error') and errors array
* @throws {Error} If hosts is not an array
*/
validate(hosts: string[], options?: {
emptyHostname?: boolean;
}): {
type: 'success' | 'error';
errors: Array<{
message: string;
package: string;
}>;
}Host Specifications:
The hosts array can contain:
'registry.npmjs.org', 'registry.yarnpkg.com''https://registry.npmjs.org''npm', 'yarn', 'verdaccio'Registry Shortcuts:
'npm' → 'registry.npmjs.org''yarn' → 'registry.yarnpkg.com''verdaccio' → 'registry.verdaccio.org'Behavior:
resolved field are automatically skipped (e.g., local filesystem packages)options.emptyHostname is set to trueUsage Examples:
const { ValidateHost, ParseLockfile } = require('lockfile-lint-api');
const parser = new ParseLockfile({ lockfilePath: './package-lock.json' });
const lockfile = parser.parseSync();
const validator = new ValidateHost({ packages: lockfile.object });
// Validate against npm registry only
const result1 = validator.validate(['npm']);
// Validate against multiple registries using shortcuts
const result2 = validator.validate(['npm', 'yarn']);
// Validate against custom registry
const result3 = validator.validate(['registry.mycompany.com']);
// Validate with full URLs
const result4 = validator.validate([
'https://registry.npmjs.org',
'https://registry.mycompany.com'
]);
// Allow empty hostnames
const result5 = validator.validate(['npm'], { emptyHostname: true });
// Check result
if (result1.type === 'success') {
console.log('All packages are from allowed hosts');
} else {
console.error('Validation errors:');
result1.errors.forEach(error => {
console.error(` ${error.package}: ${error.message}`);
});
}Validates a single package against allowed hosts.
/**
* Validates a single package against allowed hosts
* @param {string} packageName - Package key from the lockfile (e.g., 'lodash@4.17.21')
* @param {string[]} hosts - Array of allowed host strings (supports shortcuts)
* @returns {boolean} True if package is from an allowed host, false otherwise
* @throws {Error} If URL parsing fails for the package's resolved field
*/
validateSingle(packageName: string, hosts: string[]): boolean;Behavior:
true if the package's resolved URL hostname matches any of the allowed hoststrue if the package has no resolved field (e.g., local filesystem packages)'npm', 'yarn', 'verdaccio') which are automatically expanded to full hostnamesresolved field as a URL and extracts the hostname for comparisonvalidate() which catches errors silently)Usage Examples:
const { ValidateHost, ParseLockfile } = require('lockfile-lint-api');
const parser = new ParseLockfile({ lockfilePath: './package-lock.json' });
const lockfile = parser.parseSync();
const validator = new ValidateHost({ packages: lockfile.object });
// Validate specific packages
const isLodashValid = validator.validateSingle('lodash@4.17.21', ['npm']);
const isReactValid = validator.validateSingle('react@18.2.0', ['npm', 'yarn']);
if (!isLodashValid) {
console.error('lodash is not from allowed hosts');
}
// Check multiple packages programmatically
const packagesToCheck = ['lodash@4.17.21', 'react@18.2.0', 'vue@3.2.0'];
const allowedHosts = ['npm'];
packagesToCheck.forEach(packageName => {
if (!validator.validateSingle(packageName, allowedHosts)) {
console.error(`${packageName} is not from allowed hosts`);
}
});The validate() method returns a consistent result object:
interface HostValidationResult {
type: 'success' | 'error';
errors: HostValidationError[];
}
interface HostValidationError {
message: string; // Detailed error message with expected and actual hosts
package: string; // Package name that failed validation
}Success Example:
{
type: 'success',
errors: []
}Error Example:
// Single host validation
{
type: 'error',
errors: [
{
message: 'detected invalid host(s) for package: malicious-package@1.0.0\n expected: registry.npmjs.org\n actual: evil-registry.com\n',
package: 'malicious-package@1.0.0'
}
]
}
// Multiple hosts validation (array stringifies to comma-separated)
{
type: 'error',
errors: [
{
message: 'detected invalid host(s) for package: malicious-package@1.0.0\n expected: registry.npmjs.org,registry.yarnpkg.com\n actual: evil-registry.com\n',
package: 'malicious-package@1.0.0'
}
]
}Host validation is essential for preventing supply chain attacks:
Example CI/CD Usage:
const { ValidateHost, ParseLockfile } = require('lockfile-lint-api');
function validateLockfile(lockfilePath) {
const parser = new ParseLockfile({ lockfilePath });
const lockfile = parser.parseSync();
const validator = new ValidateHost({ packages: lockfile.object });
const result = validator.validate(['npm', 'registry.mycompany.com']);
if (result.type === 'error') {
console.error('Lockfile validation failed!');
result.errors.forEach(error => console.error(error.message));
process.exit(1);
}
console.log('Lockfile validation passed');
}
validateLockfile('./package-lock.json');Constructor Errors:
// Throws: 'expecting an object passed to validator constructor'
const validator = new ValidateHost(); // Missing packages
const validator = new ValidateHost({ packages: null }); // Invalid typeValidation Errors:
// Throws: 'validate method requires an array'
validator.validate('npm'); // String instead of array
validator.validate({ host: 'npm' }); // Object instead of arrayURL class to parse and extract hostnamesREGISTRY constantemptyHostname optionresolved field)// By default, empty hostnames are rejected
const validator = new ValidateHost({ packages: lockfile.object });
const result = validator.validate(['npm']);
// Packages with empty hostnames will fail
// Allow empty hostnames
const result2 = validator.validate(['npm'], { emptyHostname: true });
// Packages with empty hostnames will pass// All these are equivalent when using shortcuts
validator.validate(['npm']);
validator.validate(['registry.npmjs.org']);
validator.validate(['https://registry.npmjs.org']);
// Full URLs have hostname extracted automatically
validator.validate(['https://registry.npmjs.org/path/to/resource']);
// Only 'registry.npmjs.org' is used for comparison// Packages without resolved field are automatically skipped
{
'local-package@1.0.0': {
version: '1.0.0'
// No resolved field - automatically skipped
}
}