affaan-m/ECC · critical

Unsafe Nasiko archive: expected exactly one bounded regular

Error message

Unsafe Nasiko archive: expected exactly one bounded regular binary file.

What it means

extractQualifiedTarGzip enforces a strict allowlist while walking the tar: each entry must be the expected regular binary file (no path prefix, type '0' or NUL, size 1..64 MiB, at most once), or one of two tolerated metadata entries - a macOS AppleDouble '._name' file (<=1 MiB) or a bounded PaxHeader entry (type 'x', <=64 KiB, containing no path/linkpath overrides). Any other entry - extra files, directories, symlinks, hardlinks, devices, or Pax records that would relocate the binary - aborts extraction.

Source

Thrown at scripts/lib/nasiko-release.js:103

  let binary = null;
  while (offset + 512 <= tar.length) {
    const header = tar.subarray(offset, offset + 512);
    if (header.every(byte => byte === 0)) break;
    const name = readTarString(header, 0, 100);
    const prefix = readTarString(header, 345, 155);
    const type = String.fromCharCode(header[156] || 48);
    const rawSize = readTarString(header, 124, 12).trim();
    const size = Number.parseInt(rawSize || '0', 8);
    const start = offset + 512;
    const end = start + size;
    if (!Number.isSafeInteger(size) || size < 0 || end > tar.length) throw new Error('Nasiko archive is truncated.');
    const payload = tar.subarray(start, end);
    const isBinary = !prefix && name === expectedName && (type === '0' || type === '\0');
    const isAppleDouble = !prefix && name === `._${expectedName}` && type === '0' && size <= 1024 * 1024;
    const isPaxMetadata = !prefix && name === `PaxHeader/${expectedName}` && type === 'x' && size <= 64 * 1024
      && !/(?:^|\n)(?:path|linkpath)=/i.test(payload.toString('utf8'));
    if (isBinary && !binary && size > 0 && size <= MAX_BINARY_BYTES) binary = Buffer.from(payload);
    else if (!isAppleDouble && !isPaxMetadata) throw new Error('Unsafe Nasiko archive: expected exactly one bounded regular binary file.');
    offset = start + Math.ceil(size / 512) * 512;
  }
  if (!binary) throw new Error('Unsafe Nasiko archive: expected exactly one bounded regular binary file.');
  return binary;
}

function fetchBytes(url, options = {}) {
  const parsed = new URL(url);
  if (parsed.origin !== REGISTRY_ORIGIN || parsed.protocol !== 'https:') return Promise.reject(new Error('Nasiko download origin is not allowed.'));
  const maxBytes = options.maxBytes || MAX_ARCHIVE_BYTES;
  return new Promise((resolve, reject) => {
    const request = https.get(parsed, { headers: options.accept ? { Accept: options.accept } : {} }, response => {
      if (response.statusCode >= 300 && response.statusCode < 400) { response.resume(); reject(new Error('Nasiko registry redirects are not allowed.')); return; }
      if (response.statusCode !== 200) { response.resume(); reject(new Error(`Nasiko registry returned HTTP ${response.statusCode}.`)); return; }
      const chunks = [];
      let total = 0;
      response.on('data', chunk => {
        total += chunk.length;

View on GitHub (pinned to 06c5e118c4)

Solutions

  1. Repack the release archive with exactly one regular file named nasiko (or nasiko.exe) and nothing else
  2. Strip extra entries from the tar (for example `tar --delete` or rebuild with `tar -cf archive nasiko`)
  3. Remove path/linkpath records from PaxHeaders in the packaging pipeline
  4. Treat unexpected entries in a digest-pinned archive as a security incident and report it upstream
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await installNasiko({ version: 'v0.1.0' });
} catch (error) {
  if (/Unsafe Nasiko archive/.test(String(error.message))) {
    // Fail closed. Never loosen the entry allowlist. Inspect the archive with
    // `tar -tvf` to find the offending entry and fix the packaging (exactly one
    // regular binary, no symlinks/extra files/path-rewriting Pax records).
  }
  throw error;
}

Prevention

When it happens

Trigger: An entry in the qualified archive is not the expected binary and not whitelisted metadata: name !== expectedName (or a ustar prefix is set), type is '5' (directory), '1'/'2' (hard/symlink), or a second regular file appears; or a PaxHeader entry contains 'path=' / 'linkpath=' records that could rename or redirect the extracted file.

Common situations: Repackaging scripts that add a LICENSE or README into the archive; macOS builds leaking extra AppleDouble files beyond the bounded ones; upstream changing packaging to include directories; adversarial archives attempting path traversal or binary substitution via Pax path records.

Related errors


AI-assisted analysis of affaan-m/ECC@06c5e118c4 (2026-08-18). Data as JSON: /api/errors/17fb2a74c19b1483. Report an issue: GitHub.