# Package Integrity and Lifecycle Rules
# Category: package-integrity
# Detects deprecated, yanked, or tampered packages and suspicious metadata

- id: INT-001
  name: "npm package with install script"
  description: "npm packages with lifecycle hooks (preinstall, postinstall, etc.) execute arbitrary code during installation"
  severity: medium
  category: package-integrity
  dry-run-support: full
  condition: |
    components[
      $prop($, 'cdx:npm:hasInstallScript') = 'true'
    ]
  location: |
    { "bomRef": $."bom-ref", "purl": purl }
  message: "npm package '{{ name }}@{{ version }}' has install-time execution hooks"
  mitigation: "Review install scripts before use. Consider using --ignore-scripts or an allowlist-based approach"
  evidence: |
    {
      "riskyScripts": $prop($, 'cdx:npm:risky_scripts'),
      "isRegistryDependency": $prop($, 'cdx:npm:isRegistryDependency')
    }

- id: INT-002
  name: "npm package name or version mismatch"
  description: "Detected mismatch between expected and resolved package name or version, which may indicate dependency confusion or tampering"
  severity: high
  category: package-integrity
  dry-run-support: full
  condition: |
    components[
      $hasProp($, 'cdx:npm:nameMismatchError')
      or $hasProp($, 'cdx:npm:versionMismatchError')
    ]
  location: |
    { "bomRef": $."bom-ref", "purl": purl }
  message: "npm package '{{ name }}@{{ version }}' has a name or version mismatch"
  mitigation: "Investigate the mismatch immediately. This may indicate dependency confusion, registry tampering, or a corrupted lockfile"
  evidence: |
    {
      "nameMismatch": $prop($, 'cdx:npm:nameMismatchError'),
      "versionMismatch": $prop($, 'cdx:npm:versionMismatchError')
    }

- id: INT-003
  name: "Deprecated Go module"
  description: "Go modules marked as deprecated may contain known issues or be abandoned"
  severity: medium
  category: package-integrity
  dry-run-support: no
  condition: |
    components[
      $hasProp($, 'cdx:go:deprecated')
    ]
  location: |
    { "bomRef": $."bom-ref", "purl": purl }
  message: "Go module '{{ name }}' is deprecated: {{ $prop($, 'cdx:go:deprecated') }}"
  mitigation: "Migrate to the recommended replacement module or assess continued usage risk"
  evidence: |
    {
      "deprecationNotice": $prop($, 'cdx:go:deprecated'),
      "isIndirect": $prop($, 'cdx:go:indirect')
    }

- id: INT-004
  name: "Yanked Ruby gem in dependency tree"
  description: "Yanked gems have been removed from RubyGems, typically due to security issues or critical bugs"
  severity: high
  category: package-integrity
  dry-run-support: no
  condition: |
    components[
      $prop($, 'cdx:gem:yanked') = 'true'
    ]
  location: |
    { "bomRef": $."bom-ref", "purl": purl }
  message: "Ruby gem '{{ name }}@{{ version }}' has been yanked from RubyGems"
  mitigation: "Update to a non-yanked version immediately. Yanked gems are typically removed due to security or correctness issues"
  evidence: |
    {
      "platform": $prop($, 'cdx:gem:platform'),
      "isPrerelease": $prop($, 'cdx:gem:prerelease')
    }

- id: INT-005
  name: "npm deprecated package"
  description: "npm packages marked as deprecated may have known vulnerabilities or unmaintained code"
  severity: low
  category: package-integrity
  dry-run-support: partial
  condition: |
    components[
      $prop($, 'cdx:npm:deprecated') = 'true'
    ]
  location: |
    { "bomRef": $."bom-ref", "purl": purl }
  message: "npm package '{{ name }}@{{ version }}' is deprecated: {{ $prop($, 'cdx:npm:deprecation_notice') }}"
  mitigation: "Migrate to a maintained alternative package"
  evidence: |
    {
      "deprecationNotice": $prop($, 'cdx:npm:deprecation_notice')
    }

- id: INT-006
  name: "Dart pub uses non-default registry"
  description: "Dart packages from non-default registries may introduce unvetted code"
  severity: low
  category: package-integrity
  dry-run-support: full
  condition: |
    components[
      $hasProp($, 'cdx:pub:registry')
    ]
  location: |
    { "bomRef": $."bom-ref", "purl": purl }
  message: "Dart package '{{ name }}' sourced from non-default registry: {{ $prop($, 'cdx:pub:registry') }}"
  mitigation: "Verify registry trustworthiness"
  evidence: |
    {
      "registry": $prop($, 'cdx:pub:registry')
    }

- id: INT-007
  name: "Maven shaded/relocated package"
  description: "Maven packages with shaded or relocated classes may obscure the true dependency graph and introduce hidden vulnerabilities"
  severity: low
  category: package-integrity
  dry-run-support: full
  condition: |
    components[
      $prop($, 'cdx:maven:shaded') = 'true'
    ]
  location: |
    { "bomRef": $."bom-ref", "purl": purl }
  message: "Maven package '{{ name }}@{{ version }}' contains shaded/relocated classes"
  mitigation: "Prefer non-shaded dependency variants where available to maintain transparent dependency resolution"
  evidence: |
    {
      "unshadedNamespaces": $prop($, 'cdx:maven:unshadedNamespaces'),
      "scope": $prop($, 'cdx:maven:component_scope')
    }

- id: INT-008
  name: "README file contains hidden Unicode characters"
  description: "Hidden Unicode in README files can conceal malicious or misleading content in code review, especially inside comments"
  severity: medium
  category: package-integrity
  dry-run-support: full
  condition: |
    formulation.components[
      $prop($, 'cdx:file:kind') = 'readme'
      and $prop($, 'cdx:file:hasHiddenUnicode') = 'true'
    ]
  location: |
    {
      "bomRef": $."bom-ref",
      "file": $prop($, 'SrcFile')
    }
  message: "README file '{{ name }}' contains hidden Unicode characters"
  mitigation: "Review the file with hidden-character rendering enabled, remove suspicious bidirectional or zero-width characters, and verify comment blocks carefully before merge"
  evidence: |
    {
      "codePoints": $prop($, 'cdx:file:hiddenUnicodeCodePoints'),
      "lineNumbers": $prop($, 'cdx:file:hiddenUnicodeLineNumbers'),
      "inComments": $prop($, 'cdx:file:hiddenUnicodeInComments')
    }

- id: INT-009
  name: "npm lifecycle hook contains obfuscated or encoded execution"
  description: "Lifecycle hooks that decode base64 payloads, hide long encoded blobs, or blend obfuscation with install-time execution are high-confidence supply-chain abuse indicators"
  severity: critical
  category: package-integrity
  dry-run-support: full
  attack:
    tactics: [TA0005]
    techniques: [T1027]
  condition: |
    components[
      $prop($, 'cdx:npm:hasObfuscatedLifecycleScript') = 'true'
    ]
  location: |
    { "bomRef": $."bom-ref", "purl": purl }
  message: "npm package '{{ name }}@{{ version }}' contains obfuscated install-time lifecycle scripts '{{ $prop($, 'cdx:npm:obfuscatedLifecycleScripts') }}'"
  mitigation: "Treat encoded or obfuscated lifecycle hooks as suspicious by default. Review the decoded payload, inspect any referenced script files, and avoid installing until provenance and maintainer intent are verified"
  evidence: |
    {
      "scripts": $prop($, 'cdx:npm:obfuscatedLifecycleScripts'),
      "obfuscationIndicators": $prop($, 'cdx:npm:lifecycleObfuscationIndicators'),
      "executionIndicators": $prop($, 'cdx:npm:lifecycleExecutionIndicators'),
      "indicatorMap": $prop($, 'cdx:npm:lifecycleIndicatorMap')
    }

- id: INT-010
  name: "Yanked Cargo crate in dependency tree"
  description: "Crates yanked from crates.io are often removed because of security, correctness, or ecosystem breakage issues"
  severity: high
  category: package-integrity
  dry-run-support: no
  condition: |
    components[
      $prop($, 'cdx:cargo:yanked') = 'true'
    ]
  location: |
    { "bomRef": $."bom-ref", "purl": purl }
  message: "Cargo crate '{{ name }}@{{ version }}' has been yanked from crates.io"
  mitigation: "Update to a non-yanked release and review any recent publisher or release-cadence changes before upgrading"
  evidence: |
    {
      "publisher": $prop($, 'cdx:cargo:publisher'),
      "priorPublisher": $prop($, 'cdx:cargo:priorPublisher'),
      "publishTime": $prop($, 'cdx:cargo:publishTime'),
      "releaseGapDays": $prop($, 'cdx:cargo:releaseGapDays')
    }

- id: INT-011
  name: "Rust project uses native Cargo build surface"
  description: "Cargo build scripts and native build helpers expand execution surface during build and deserve additional review before release"
  severity: medium
  category: package-integrity
  dry-run-support: full
  condition: |
    formulation.components[
      $prop($, 'cdx:rust:buildTool') = 'cargo'
      and $prop($, 'cdx:cargo:hasNativeBuild') = 'true'
    ]
  location: |
    {
      "bomRef": $."bom-ref",
      "file": $prop($, 'SrcFile')
    }
  message: "Rust project '{{ name }}' uses Cargo build.rs or native build helpers"
  mitigation: "Review `build.rs`, native build dependencies, and release workflows to ensure the build remains hermetic and expected"
  evidence: |
    {
      "buildScript": $prop($, 'cdx:cargo:buildScript'),
      "nativeBuildIndicators": $prop($, 'cdx:cargo:nativeBuildIndicators'),
      "releaseProfiles": $prop($, 'cdx:cargo:releaseProfiles')
    }

- id: INT-012
  name: "Rust native build uses mutable Cargo setup action"
  description: "Native Cargo build surfaces deserve additional scrutiny when the workflow relies on mutable Cargo toolchain setup actions instead of immutable SHA-pinned references"
  severity: medium
  category: package-integrity
  dry-run-support: full
  condition: |
    formulation.components[
      $prop($, 'cdx:rust:buildTool') = 'cargo'
      and $prop($, 'cdx:cargo:hasNativeBuild') = 'true'
      and $count($$.components[
        $prop($, 'cdx:github:action:ecosystem') = 'cargo'
        and $contains($prop($, 'cdx:github:action:role'), 'toolchain')
        and $prop($, 'cdx:github:action:versionPinningType') != 'sha'
      ]) > 0
    ]
  location: |
    {
      "bomRef": $."bom-ref",
      "file": $prop($, 'SrcFile')
    }
  message: "Rust project '{{ name }}' combines native Cargo build surfaces with mutable Cargo toolchain setup actions"
  mitigation: "Prefer SHA-pinned Cargo toolchain setup actions for release workflows, especially when build.rs or native helpers run during CI"
  evidence: |
    {
      "buildScript": $prop($, 'cdx:cargo:buildScript'),
      "buildScriptCapabilities": $prop($, 'cdx:cargo:buildScriptCapabilities'),
      "toolchainActions": $$.components[
        $prop($, 'cdx:github:action:ecosystem') = 'cargo'
        and $contains($prop($, 'cdx:github:action:role'), 'toolchain')
        and $prop($, 'cdx:github:action:versionPinningType') != 'sha'
      ].$prop($, 'cdx:github:action:uses')
    }

- id: INT-013
  name: "Rust native build is exercised by Cargo workflow steps"
  description: "Cargo workflows that execute build/test/package/publish steps against native build surfaces increase the impact of build-script or native-helper changes"
  severity: medium
  category: package-integrity
  dry-run-support: full
  condition: |
    formulation.components[
      $prop($, 'cdx:rust:buildTool') = 'cargo'
      and $prop($, 'cdx:cargo:hasNativeBuild') = 'true'
      and (
        $contains($prop($, 'cdx:cargo:buildScriptCapabilities'), 'process-execution')
        or $contains($prop($, 'cdx:cargo:buildScriptCapabilities'), 'network-access')
        or $contains($prop($, 'cdx:cargo:nativeBuildIndicators'), '-sys')
      )
      and $count($$.components[
        $prop($, 'cdx:github:step:usesCargo') = 'true'
        and (
          $contains($prop($, 'cdx:github:step:cargoSubcommands'), 'build')
          or $contains($prop($, 'cdx:github:step:cargoSubcommands'), 'test')
          or $contains($prop($, 'cdx:github:step:cargoSubcommands'), 'package')
          or $contains($prop($, 'cdx:github:step:cargoSubcommands'), 'publish')
        )
      ]) > 0
    ]
  location: |
    {
      "bomRef": $."bom-ref",
      "file": $prop($, 'SrcFile')
    }
  message: "Rust project '{{ name }}' runs Cargo build/test/package workflow steps against a native build surface"
  mitigation: "Review build.rs, native helper crates, and workflow command scope before merging or publishing releases that exercise native build logic"
  evidence: |
    {
      "buildScriptCapabilities": $prop($, 'cdx:cargo:buildScriptCapabilities'),
      "nativeBuildIndicators": $prop($, 'cdx:cargo:nativeBuildIndicators'),
      "cargoCommands": $$.components[
        $prop($, 'cdx:github:step:usesCargo') = 'true'
        and (
          $contains($prop($, 'cdx:github:step:cargoSubcommands'), 'build')
          or $contains($prop($, 'cdx:github:step:cargoSubcommands'), 'test')
          or $contains($prop($, 'cdx:github:step:cargoSubcommands'), 'package')
          or $contains($prop($, 'cdx:github:step:cargoSubcommands'), 'publish')
        )
      ].$prop($, 'cdx:github:step:command')
    }

- id: INT-014
  name: "Collider package missing valid wrap hash pin"
  description: "Collider lock entries should carry a SHA-256 wrap_hash so the selected wrap file remains integrity-pinned and reproducible"
  severity: high
  category: package-integrity
  dry-run-support: full
  condition: |
    components[
      $hasProp($, 'cdx:collider:dependencyKind')
      and $prop($, 'cdx:collider:hasWrapHash') = 'false'
    ]
  location: |
    { "bomRef": $."bom-ref", "purl": purl }
  message: "Collider package '{{ name }}@{{ version }}' is missing a valid wrap hash integrity pin"
  mitigation: "Recreate collider.lock with valid wrap_hash values and verify the lockfile against the repository before release"
  evidence: |
    {
      "wrapHash": $prop($, 'cdx:collider:wrapHash'),
      "wrapHashInvalid": $prop($, 'cdx:collider:wrapHashInvalid'),
      "origin": $prop($, 'cdx:collider:origin'),
      "dependencyKind": $prop($, 'cdx:collider:dependencyKind')
    }
