{"record":{"id":"faf71dc61da08d08","repo":"paperclipai/paperclip","slug":"migrator-dependency-has-no-strong-integrity-pin","errorCode":null,"errorMessage":"Migrator dependency has no strong integrity pin.","messagePattern":"Migrator dependency has no strong integrity pin\\.","errorType":"console","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/cloud-migrator-artifacts.mjs","lineNumber":57,"sourceCode":"export function assertLockfile(lock, manifest) {\n  const version = manifest.packageVersion;\n  if (lock?.lockfileVersion !== 3 || !lock.packages || Array.isArray(lock.packages) ||\n      JSON.stringify(lock.packages[\"\"]?.dependencies) !== JSON.stringify({ \"@paperclipai/db\": version })) throw new Error(\"Invalid migrator lockfile root.\");\n  for (const name of names) {\n    const pin = lock.packages[`node_modules/@paperclipai/${name}`];\n    const expected = manifest.packages[name];\n    if (pin?.version !== version || pin.integrity !== expected.integrity || pin.resolved !== expected.url || pin.link || pin.inBundle) throw new Error(\"Migrator lockfile package pin mismatch.\");\n  }\n  if (lock.packages[\"node_modules/@paperclipai/db\"].dependencies?.[\"@paperclipai/shared\"] !== version) throw new Error(\"Migrator shared dependency mismatch.\");\n  for (const [key, entry] of Object.entries(lock.packages)) {\n    if (key === \"\") continue;\n    if (!entry || typeof entry !== \"object\" || entry.link) throw new Error(\"Invalid migrator lockfile entry.\");\n    if (/(?:^|\\/)node_modules\\/@paperclipai\\/[^/]+$/.test(key) && !names.some((name) => key === `node_modules/@paperclipai/${name}`)) throw new Error(\"Unexpected internal migrator dependency.\");\n    if (entry.inBundle === true) {\n      if (!key.startsWith(\"node_modules/@paperclipai/db/node_modules/\")) throw new Error(\"Unexpected bundled dependency.\");\n      continue;\n    }\n    if (!/^sha512-[A-Za-z0-9+/]{86}==$/.test(entry.integrity ?? \"\")) throw new Error(\"Migrator dependency has no strong integrity pin.\");\n    if (names.some((name) => key === `node_modules/@paperclipai/${name}`)) continue;\n    const url = new URL(entry.resolved);\n    if (url.origin !== \"https://registry.npmjs.org\" || url.username || url.password || url.search || url.hash) throw new Error(\"Migrator dependency must resolve to npm.\");\n  }\n}\n\nexport function buildBundle(directory, sha, { exec = execFileSync } = {}) {\n  versionFor(sha);\n  directory = path.resolve(directory);\n  const packages = {};\n  for (const name of names) {\n    const bytes = readFileSync(path.join(directory, `${name}.tgz`));\n    assertMetadata(tarManifest(bytes), `@paperclipai/${name}`, sha);\n    packages[name] = descriptor(bytes, \"tgz\");\n  }\n  const scratch = mkdtempSync(path.join(os.tmpdir(), \"cloud-migrator-lock-\"));\n  try {\n    for (const name of names) copyFileSync(path.join(directory, `${name}.tgz`), path.join(scratch, `${name}.tgz`));","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/scripts/cloud-migrator-artifacts.mjs#L39-L75","documentation":"Every non-bundled lockfile entry must carry a strong sha512 integrity pin matching /^sha512-[A-Za-z0-9+/]{86}==$/. This error means an entry's integrity is missing, malformed, or uses a weaker hash, so the artifact cannot guarantee the dependency's bytes are authentic. It is a supply-chain hardening check.","triggerScenarios":"assertLockfile sees an entry (outside root and bundled paths) whose integrity is undefined, empty, a sha1 hash, or otherwise not a canonical 86-char base64 sha512 string.","commonSituations":"Older lockfiles or registries supplying sha1-only integrity; a hand-written or merged lockfile entry lacking integrity; npm config like strict-ssl=false or legacy peer resolution producing weaker pins; lockfileVersion < 3 data mixed in.","solutions":["Regenerate the lockfile with the `build` command against registry.npmjs.org so every entry gets a sha512 integrity field","Check the lockfile is version 3 and entries were not copied from an older sha1-era lockfile","Do not hand-edit package-lock.json; if an entry lacks integrity, resolve it from the official registry and re-lock"],"exampleFix":"// before\n\"some-dep\": { \"version\": \"1.0.0\", \"resolved\": \"https://registry.npmjs.org/some-dep/-/some-dep-1.0.0.tgz\", \"integrity\": \"sha1-abc...\" }\n// after\n\"some-dep\": { \"version\": \"1.0.0\", \"resolved\": \"https://registry.npmjs.org/some-dep/-/some-dep-1.0.0.tgz\", \"integrity\": \"sha512-<86 base64 chars>==\" }","handlingStrategy":"validation","validationCode":"const STRONG = /^sha512-[A-Za-z0-9+/]{86}==$/;\nfor (const [key, entry] of Object.entries(lock.packages ?? {})) {\n  if (key === \"\" || entry?.inBundle === true) continue;\n  if (!STRONG.test(entry?.integrity ?? \"\")) throw new Error(`weak/missing integrity for ${key}`);\n}","typeGuard":"const hasStrongIntegrity = (entry) => /^sha512-[A-Za-z0-9+/]{86}==$/.test(entry?.integrity ?? \"\");","tryCatchPattern":"try {\n  assertLockfile(lock, manifest);\n} catch (err) {\n  if (err.message === \"Migrator dependency has no strong integrity pin.\") throw new Error(\"Regenerate the lockfile against registry.npmjs.org to obtain sha512 pins\");\n  throw err;\n}","preventionTips":["Always build against the official registry so npm records sha512 integrity","Do not mix entries from older sha1-era lockfiles","Keep lockfileVersion 3","Treat missing integrity in any lockfile entry as a blocker, not a warning"],"tags":["supply-chain","integrity","lockfile","security"],"backgroundTag":"checksum-mismatch","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}