# How to Manage False Positives with SkillSpector Baseline Suppression: A Team Guide

> Learn how to manage false positives with SkillSpector baseline suppression. Filter CI scan noise using YAML rules and SHA-256 fingerprints for cleaner, shared repositories.

- Repository: [NVIDIA Corporation/SkillSpector](https://github.com/NVIDIA/SkillSpector)
- Tags: how-to-guide
- Published: 2026-07-07

---

**SkillSpector baseline suppression filters false positives using YAML-based rules for glob patterns and exact SHA-256 fingerprints, enabling teams to maintain clean CI scans across shared repositories.**

NVIDIA's SkillSpector open-source security scanner uses a baseline file to exclude findings that teams classify as non-vulnerabilities. Understanding how to configure this suppression system allows development teams to eliminate noise while preserving critical security signals across collaborative workflows.

## How Baseline Suppression Works in SkillSpector

SkillSpector implements a dual-mechanism approach in [`src/skillspector/suppression.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/suppression.py) to distinguish between acceptable and actionable findings. The system evaluates every finding against **rules** (pattern-based) and **fingerprints** (exact-match) to determine suppression status.

### Glob-Based Rules for Systematic Patterns

The `rules` array contains human-authored patterns that match a finding's **id**, **path**, and/or **message**. A rule suppresses a finding only when all specified fields satisfy the glob pattern. The `_match_glob` function normalizes `**` to `*` and executes case-insensitive `fnmatch` matching (see module docstring lines 47-50). An empty rule never matches, preventing accidental catch-all suppressions.

**When to use:** Ideal for systematic false positives, such as excluding rule "SQP-1" globally or filtering specific paths like `*deploy-topology*/SKILL.md`.

### Exact Fingerprints for Specific Findings

The `fingerprints` array stores stable SHA-256 hashes generated by `finding_fingerprint()` (lines 85-103). This hash incorporates the rule ID, file path, line range, and message content. Only new, unseen fingerprints survive subsequent scans, making this mechanism perfect for incremental CI workflows where teams want to freeze the current finding set.

**When to use:** Best for "accept-as-is" findings where the underlying code won't change, ensuring any modification resurfaces the finding for re-evaluation.

### Core Implementation Details

In [`src/skillspector/suppression.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/suppression.py), the `Baseline` class orchestrates suppression logic:

- **`SuppressionRule.matches()`** evaluates whether a finding satisfies all rule criteria (rule_id, path, message)
- **`Baseline.reason_for()`** (lines 50-58) returns the suppression reason for matched findings
- **`partition_findings()`** (lines 27-46) splits findings into kept and suppressed subsets
- **`load_baseline()`** (lines 9-24) parses the YAML/JSON baseline file

The report node in [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py) (lines 408-410) excludes suppressed findings from SARIF output and risk score calculations.

## Managing False Positives Across Team Scans

Establishing a shared suppression policy requires version-controlling your baseline and adhering to granular matching strategies.

### Step-by-Step Baseline Creation Workflow

1. **Run an initial scan without baselines** to capture all current findings.

2. **Generate a baseline file** from existing findings:
   
   ```bash
   skillspector baseline <skill-dir> --no-llm --output baseline.yaml
   ```

   
   The CLI in [`src/skillspector/cli.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/cli.py) invokes `build_baseline_dict()` (lines 49-66) and `dump_baseline()` (lines 69-79) to fingerprint every finding.

3. **Refine with human-authored rules** by editing [`baseline.yaml`](https://github.com/NVIDIA/SkillSpector/blob/main/baseline.yaml) to address recurring false positives:
   
   ```yaml
   version: 1
   rules:
     - id: "SQP-1"
       reason: "Trigger-phrase nit, not a vulnerability"
     - id: "SSD-2"
       path: "*deploy-topology*/SKILL.md"
       message: "*run the exploit*"
       reason: "False positive: test-workflow phrase"
   fingerprints:
     - hash: "sha256:1a2b3c4d5e6f7081"
       rule_id: "SDI-2"
       file: "baas-build-analysis/SKILL.md"
       reason: "Accepted 2026-06-19 — first-party env detection"
   ```

4. **Commit the baseline** to your repository (e.g., [`skill-configs/baseline.yaml`](https://github.com/NVIDIA/SkillSpector/blob/main/skill-configs/baseline.yaml)) so all team members and CI pipelines use identical suppression logic.

5. **Execute filtered scans** using the shared baseline:
   
   ```bash
   skillspector scan <skill-dir> --baseline baseline.yaml
   ```

### Team Collaboration Best Practices

- **Commit the baseline to version control** to ensure every CI run and developer scan applies the same policy.
- **Keep rules specific** by including `path` or `message` constraints to avoid unintentionally silencing legitimate vulnerabilities.
- **Prefer fingerprints for one-off acceptances** since any code or message change generates a new fingerprint, forcing re-evaluation.
- **Automate baseline regeneration** in CI steps that run `skillspector baseline` on clean scans, updating the file only after explicit team review.
- **Monitor suppression statistics** via the report node summary (`Suppressed by baseline: N` at line 408) to track filtering volume.

## Programmatic API and CLI Usage

SkillSpector exposes Python APIs for custom suppression workflows:

```python

# Load and query a baseline

from skillspector.suppression import load_baseline
from skillspector.models import Finding

baseline = load_baseline("baseline.yaml")

f = Finding(rule_id="SQP-1", file="my/skill/SKILL.md", message="Trigger phrase")
reason = baseline.reason_for(f)  # Returns: "Trigger-phrase nit, not a vulnerability"

```

```python

# Programmatically create baselines

from skillspector.suppression import build_baseline_dict, dump_baseline

baseline_dict = build_baseline_dict([f], reason="Accepted in CI")
dump_baseline(baseline_dict, "baseline.yaml")

```

```bash

# Complete CLI round-trip

skillspector baseline my_skill --no-llm --output baseline.yaml
skillspector scan my_skill --baseline baseline.yaml

```

## Summary

- **SkillSpector** uses two complementary suppression mechanisms: glob-based **rules** for patterns and SHA-256 **fingerprints** for exact matches.
- The core logic resides in [`src/skillspector/suppression.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/suppression.py), with `partition_findings()` and `Baseline.reason_for()` handling the filtering decisions.
- Teams should commit baselines to version control and use specific rules to avoid over-suppression.
- Fingerprints provide immutable acceptance of specific findings, while rules handle systematic false positive categories.
- Suppressed findings are excluded from SARIF reports and risk scores according to [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py) logic.

## Frequently Asked Questions

### What is the difference between rules and fingerprints in SkillSpector?

**Rules** use glob patterns to match multiple findings based on ID, file path, or message content, making them ideal for systematic false positives. **Fingerprints** store exact SHA-256 hashes of specific findings (generated from rule ID, file, line range, and message), ensuring only identical findings are suppressed. Fingerprints automatically surface findings when underlying code changes, while rules persist until manually removed.

### How do I create a baseline file for my team?

Run `skillspector baseline <skill-dir> --no-llm --output baseline.yaml` to generate a file containing fingerprints of all current findings. Edit this file to add human-authored rules for recurring false positives, then commit it to your repository. Future scans using `skillspector scan <skill-dir> --baseline baseline.yaml` will filter matched findings from reports and risk scores.

### Why are my suppressed findings still appearing in the scan?

Verify that your baseline file path is correct and accessible to the CLI. Check that rule glob patterns use correct syntax (`*` matches across path separators, `**` normalizes to `*`). For fingerprint-based suppressions, any change to the finding's rule ID, file path, line numbers, or message text generates a new SHA-256 hash, causing the finding to resurface for re-evaluation.

### How does SkillSpector calculate finding fingerprints?

The `finding_fingerprint()` function in [`src/skillspector/suppression.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/suppression.py) (lines 85-103) computes a deterministic SHA-256 hash from the finding's rule ID, absolute file path, line range, and message content. This ensures that any modification to the underlying code or LLM-generated message invalidates the fingerprint, preventing stale suppressions from masking new vulnerabilities.