or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

examples

edge-cases.mdreal-world-scenarios.md
index.md
tile.json

validate-host.mddocs/reference/

Host Validation

The ValidateHost class validates that all package resources come from whitelisted hosts, preventing packages from unauthorized registries.

Capabilities

ValidateHost Constructor

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
});

Validate All Packages

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:

  • Full hostnames: 'registry.npmjs.org', 'registry.yarnpkg.com'
  • Full URLs (hostname will be extracted): 'https://registry.npmjs.org'
  • Registry shortcuts: 'npm', 'yarn', 'verdaccio'

Registry Shortcuts:

  • 'npm''registry.npmjs.org'
  • 'yarn''registry.yarnpkg.com'
  • 'verdaccio''registry.verdaccio.org'

Behavior:

  • Packages without a resolved field are automatically skipped (e.g., local filesystem packages)
  • Empty hostnames are rejected unless options.emptyHostname is set to true
  • URL parsing errors are silently ignored (package is considered valid)

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 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}`);
  });
}

Validate Single Package

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:

  • Returns true if the package's resolved URL hostname matches any of the allowed hosts
  • Returns true if the package has no resolved field (e.g., local filesystem packages)
  • Supports registry shortcuts ('npm', 'yarn', 'verdaccio') which are automatically expanded to full hostnames
  • Parses the package's resolved field as a URL and extracts the hostname for comparison
  • May throw an error if URL parsing fails (unlike validate() 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`);
  }
});

Validation Result Structure

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'
    }
  ]
}

Security Use Cases

Host validation is essential for preventing supply chain attacks:

  1. Prevent Malicious Registries: Ensure all packages come from trusted registries
  2. Corporate Policy Enforcement: Restrict packages to approved internal registries
  3. Compliance Requirements: Validate packages only come from audited sources
  4. CI/CD Integration: Automatically fail builds if packages from unauthorized hosts are detected

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');

Error Handling

Constructor Errors:

// Throws: 'expecting an object passed to validator constructor'
const validator = new ValidateHost();  // Missing packages
const validator = new ValidateHost({ packages: null });  // Invalid type

Validation Errors:

// Throws: 'validate method requires an array'
validator.validate('npm');  // String instead of array
validator.validate({ host: 'npm' });  // Object instead of array

Implementation Notes

  1. URL Parsing: Uses Node.js URL class to parse and extract hostnames
  2. Registry Shortcuts: Automatically expanded using the REGISTRY constant
  3. Hostname Extraction: Handles both simple hostnames and full URLs
  4. Empty Hostnames: Can be allowed or rejected based on emptyHostname option
  5. Local Packages: Automatically skipped (packages without resolved field)
  6. Error Tolerance: URL parsing errors are silently caught to avoid false positives

Edge Cases

Handling Empty Hostnames

// 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

Handling URL Variations

// 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

Handling Local Packages

// Packages without resolved field are automatically skipped
{
  'local-package@1.0.0': {
    version: '1.0.0'
    // No resolved field - automatically skipped
  }
}