Skip to content

Suppression path matching fails when source-dir contains symlinks (e.g. macOS /tmp) #348

Description

@rafaelpereyra

Summary

Suppressions silently fail to match when the SARIF URI emitted by a scanner is an absolute path whose prefix differs from source_dir.resolve(), even though both refer to the same file. This happens reliably on macOS for any scan whose source-dir is under /tmp (because /tmp is a symlink to /private/tmp), and would happen any time the source directory contains symlinks anywhere in its path.

The result: the finding is reported as actionable AND the suppression is reported as "unused" by the unused-suppressions reporter.

Reproduction (macOS)

mkdir -p /tmp/ash-bug-repro/src/.ash
mkdir -p /tmp/ash-bug-repro/rules/problem-based-packs/insecure-transport/js-node

cat > /tmp/ash-bug-repro/src/insecure.js <<'JS'
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;
const https = require('https');
const agent = new https.Agent({rejectUnauthorized: false});
JS

curl -sL https://raw.githubusercontent.com/opengrep/opengrep-rules/main/problem-based-packs/insecure-transport/js-node/bypass-tls-verification.yaml \
  -o /tmp/ash-bug-repro/rules/problem-based-packs/insecure-transport/js-node/bypass-tls-verification.yaml

cat > /tmp/ash-bug-repro/.ash/.ash.yaml <<'YAML'
project_name: ash-bug-repro
fail_on_findings: false
global_settings:
  severity_threshold: LOW
  suppressions:
    - rule_id: "rules.problem-based-packs.insecure-transport.js-node.bypass-tls-verification"
      path: "src/insecure.js"
      reason: "Test"
scanners:
  opengrep:
    enabled: true
    options:
      config: rules
YAML

ash --source-dir /tmp/ash-bug-repro --mode local

Observed

  • Opengrep produces 2 findings, both reported as actionable.
  • reports/ash.unused-suppressions.json reports the suppression as unused.
  • ash_aggregated_results.json shows the SARIF URI as /tmp/ash-bug-repro/src/insecure.js (absolute), and used_suppressions is empty.

Expected

  • Both findings suppressed.
  • used_suppressions contains the suppression id.
  • Unused-suppressions report shows it as used.

Verification that this is the symlink issue

Re-running the same repro under a non-symlinked path (e.g. /Users/<me>/ash-bug-repro) produces the correct behavior: SARIF URIs get normalized to src/insecure.js, both findings are suppressed, and used_suppressions contains the entry. Only the /tmp (or any other symlinked) path fails.

Root cause

In automated_security_helper/utils/sarif_utils.py::apply_suppressions_to_sarif, the URI normalization computes:

_source_dir_prefix = str(plugin_context.source_dir.resolve()).replace("\\", "/") + "/"

and then strips this prefix from each SARIF URI. On macOS, Path("/tmp/x").resolve() returns /private/tmp/x, so the prefix becomes /private/tmp/x/ while the SARIF URI emitted by opengrep is /tmp/x/src/insecure.js. The prefix never matches, and the URI is left as an absolute path, which then fails to match the suppression's relative path.

The same issue affects:

  • the ignore_paths evaluation in the same function (lines ~478)
  • any scanner whose SARIF URIs use the unresolved path the user invoked ASH with

The function already handles a few Windows-specific variants (/D:/..., drive-letter-stripped paths) but does not handle the symlink case.

Suggested fix

Resolve symlinks on the SARIF URI before prefix-stripping, or compute multiple prefix candidates (e.g. both source_dir and source_dir.resolve()), or fall back to comparing Path(uri).resolve() against source_dir.resolve(). Care needed for offline opengrep paths (<basename>/...) that the existing code already handles separately.

Impact

  • High user-visible: suppressions silently no-op when the source directory contains symlinks anywhere in its path. Users see findings they thought were suppressed AND see their suppression flagged as "unused" — the symptom looks like a config bug, not a path-normalization bug.
  • Affects any macOS user scanning under /tmp (common in CI scripts, smoke tests, sandboxes).
  • Affects any environment where the source directory traverses a symlink (mount points, container bind mounts, etc.).

Environment

  • ASH version: 3.5.2 (verified locally)
  • macOS (any version where /tmp is symlinked to /private/tmp)
  • Scanner: opengrep (but the bug is in shared SARIF post-processing, so any scanner emitting absolute paths is affected)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions