affaan-m/ECC · error · Error

Unsafe Nasiko archive: expected exactly one bounded regular…

Error message

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

What it means

The extractor is a strict allowlist parser: it accepts exactly one bounded regular binary matching the expected name (plus optional AppleDouble and PaxHeader metadata entries). Any other entry — or a second copy of the binary — causes this error. The final post-loop check also throws it when the loop ended without ever capturing a binary payload (e.g., zero-size binary or no matching entry).

Solutions

  1. Verify the downloaded version matches the version whose inner filename you expect; re-fetch the matching release.
  2. Re-download from the official registry — a modified archive likely contains added entries.
  3. Check MAX_BINARY_BYTES versus the actual binary size; if the artifact legitimately grew, update the bound in the code/config.
  4. Inspect the archive listing (tar -tvf) to see which entry violates the allowlist.

Example fix

// before
const binary = extractQualifiedTarGzip(tar); // archive contains README.md too
// after
const entries = listTarEntries(tar);
if (entries.some(e => !ALLOWED_NAMES.has(e.name))) throw new Error('Archive has unexpected entries: ' + entries.map(e => e.name));
const binary = extractQualifiedTarGzip(tar);
Defensive patterns

Strategy: validation

Validate before calling

const names = listTarEntries(tar).map(e => e.name); if (names.filter(n => n === expectedName).length !== 1) throw new Error('Archive must contain exactly one entry named ' + expectedName);

Try / catch

try { return extractQualifiedTarGzip(tar, expectedName); } catch (err) { if (err.message.includes('expected exactly one bounded regular binary')) logAndFailWithListing(tar); throw err; }

Prevention

When it happens

Trigger: Calling extractQualifiedTarGzip with an archive containing: (a) an unexpected extra file entry, (b) a binary entry larger than MAX_BINARY_BYTES or with size 0, (c) a duplicate expected-name entry, or (d) no entry matching expectedName at all.

Common situations: Registry publishes a repackaged tarball with extra files (LICENSE, README) inside; the release pipeline changed the inner filename; a supply-chain tampering attempt embedding extra payloads; version mismatch between expected name and actual artifact.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/17fb2a74c19b1483. Report an issue: GitHub.

Appendix: source

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

    }
    const name = readTarString(header, 0, 100);
    const prefix = readTarString(header, 345, 155);
    const type = String.fromCharCode(header[156] || 48);
    const size = readTarOctal(header, 124, 12);
    const start = offset + 512;
    const end = start + size;
    const paddedEnd = start + Math.ceil(size / 512) * 512;
    if (!Number.isSafeInteger(end) || paddedEnd > tar.length) throw new Error('Nasiko archive is truncated.');
    const payload = tar.subarray(start, end);
    if (!tar.subarray(end, paddedEnd).every(byte => byte === 0)) {
      throw new Error('Unsafe Nasiko archive: nonzero tar padding.');
    }
    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 = paddedEnd;
  }
  if (!terminated) throw new Error('Unsafe Nasiko archive: missing complete tar terminator.');
  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 => {

View on GitHub (pinned to 8321021c54)