{"record":{"id":"ff32b942149b9499","repo":"denoland/deno","slug":"prefix-linter-plugin-must-be-an-object","errorCode":null,"errorMessage":"${prefix}Linter plugin must be an object","messagePattern":"(.+?)Linter plugin must be an object","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"cli/js/40_lint.js","lineNumber":483,"sourceCode":"    }\n  }\n\n  return plugins.map((plugin, i) => installPlugin(plugin, specifiers?.[i]));\n}\n\n/**\n * @param {Deno.lint.Plugin} plugin\n * @param {string} [specifier] The specifier the plugin was loaded from, if any.\n */\nfunction installPlugin(plugin, specifier) {\n  // When the plugin was loaded from a specifier (i.e. `lint.plugins` in the\n  // config), prefix validation errors with it so the user knows which plugin\n  // is misbehaving instead of getting an anonymous error.\n  const prefix = typeof specifier === \"string\"\n    ? `Failed to load lint plugin '${specifier}': `\n    : \"\";\n  if (typeof plugin !== \"object\") {\n    throw new Error(`${prefix}Linter plugin must be an object`);\n  }\n  if (typeof plugin.name !== \"string\") {\n    throw new Error(`${prefix}Linter plugin name must be a string`);\n  }\n  if (!/^[a-z-]+$/.test(plugin.name)) {\n    throw new Error(\n      `${prefix}Linter plugin name must only contain lowercase letters (a-z) or hyphens (-).`,\n    );\n  }\n  if (plugin.name.startsWith(\"-\") || plugin.name.endsWith(\"-\")) {\n    throw new Error(\n      `${prefix}Linter plugin name must start and end with a lowercase letter.`,\n    );\n  }\n  if (plugin.name.includes(\"--\")) {\n    throw new Error(\n      `${prefix}Linter plugin name must not have consequtive hyphens.`,\n    );","sourceCodeStart":465,"sourceCodeEnd":501,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/cli/js/40_lint.js#L465-L501","documentation":"A path-traversal (Zip Slip) guard inside the jsr-compat tarball extraction: each entry's path is stripped of its leading package-root component, and the stripped remainder is rejected if it is absolute or contains `..`/root components — i.e. if the entry would resolve outside the destination directory. This is a deliberate security refusal, not a bug to work around; a hit strongly suggests a maliciously or corruptly packed tarball.","triggerScenarios":"Extracting a jsr compat tarball (downloaded from npm.jsr.io during `deno check`/install with stock tsc) that contains entries like `pkg/../../evil.sh`, absolute paths (`/etc/...`), or root-component paths. The check operates on the path after skipping the first component, so only genuinely escaping entries are refused.","commonSituations":"Supply-chain attack via a compromised package; a corrupted download producing garbage paths; a broken pack tool emitting absolute entry names. Genuine packages virtually never trip this guard.","solutions":["Do NOT bypass the guard — treat it as a supply-chain red flag","Identify which dependency it came from (the log names the package being installed just above) and inspect it: check its JSR page, source repo, and recent version bumps","Pin the dependency to the last known-good version in deno.json / lockfile until the package is fixed","Report the package to JSR abuse (security@jsr.io) and rotate any secrets if the code was ever executed"],"exampleFix":"// deno.json — before (pulling compromised latest)\n\"imports\": { \"pkg\": \"jsr:@evil/pkg\" }\n\n// after (pin known-good, audit)\n\"imports\": { \"pkg\": \"jsr:@evil/pkg@0.9.1\" }  // last clean version, pending maintainer fix","handlingStrategy":"validation","validationCode":"# Audit dependency tarballs for traversal entries before wide rollout\n# (the guard itself is the runtime validation — this is defense in depth)\ndeno info --json 2>/dev/null | jq '.modules[].specifier'  # review your remote deps","typeGuard":null,"tryCatchPattern":"Never catch-and-continue this error. In automation, match /Refusing to extract tar entry outside dest/ and escalate immediately: halt the build, notify security, pin the offending dependency to its last known-good version, and audit lockfile diffs.","preventionTips":["Treat this error as a supply-chain alert, never as noise to suppress","Commit and review lockfile diffs in CI so new package versions get human eyes","Subscribe to JSR/npm advisories for your dependencies","If triggered, rotate credentials that the compromised package could have touched"],"tags":["security","path-traversal","tarball","supply-chain","zip-slip"],"backgroundTag":"zip-slip-path-traversal","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}