This document explains our compliance status with OSSF Scorecard checks and documents any accepted risks or false positives.
Install to Claude Code
npx -y skills add tools-only/X-Skills --skill 272-security_scorecard_a19e5df8 --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of 272 Security Scorecard A19e5df8?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/tools-only-272-security-scorecard-a19e5df8)More formats (shields.io, HTML) on the badges page.
# OSSF Scorecard Compliance Notes
This document explains our compliance status with OSSF Scorecard checks
and documents any accepted risks or false positives.
## Current Score: 8/10 for Pinned-Dependencies
**Summary:**
- ✅ 196/196 GitHub Actions pinned by SHA
- ✅ 4/4 Container images pinned by digest
- ✅ 2/2 Security tools use official SHA-pinned actions (pip-audit, zizmor)
- ⚠️ 2/27 pip commands pinned by hash (remainder version-pinned — accepted risk)
- ⚠️ 21/24 npm commands pinned (3 are operational commands)
- ⚠️ 1 false positive for downloadThenRun
- ⚠️ APT packages intentionally unpinned (base image controls versions)
## Pinned-Dependencies
### GitHub Actions: COMPLIANT ✅
All GitHub Actions use commit SHA pinning (40-character hex):
```yaml
# Examples from our workflows:
actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6
step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
```
### Docker Images: COMPLIANT ✅
All Docker images use SHA256 digest pinning:
```yaml
# Examples from our workflows and docker-compose:
python:3.13.9-slim@sha256:326df678c20c78d465db501563f3492d17c42a4afe33a1f2bf5406a1d56b0e86
redis:alpine@sha256:8360960f5fb56a282d78686203dd875862cd4b52a4184c17ac753690252d6d31
node:20-alpine@sha256:bcd88137d802e2482c9df3cdec71e0431857ebbbdba6973776b5593214056d86
```
### Official GitHub Actions: COMPLIANT ✅
Two security tools use official GitHub Actions with SHA-pinning,
which provides equivalent security to hash-pinned pip installs:
| Tool | Action | SHA |
|------|--------|-----|
| pip-audit | `pypa/gh-action-pip-audit` | `f9e2142a494d0d5d0d84e508e22a802af02cd086` |
| zizmor | `zizmorcore/zizmor-action` | `135698455da5c3b3e55f73f4419e481ab68cdd95` |
These official actions run their tools in containers with internal integrity verification,
which OSSF Scorecard accepts as equivalent to hash pinning.
> **Note:** Checkov previously used `bridgecrewio/checkov-action` but was reverted to CLI
> installation (`pip install checkov==3.2.499`) due to a known bug with multiple
> consecutive action calls. See [checkov-action#170](https://github.com/bridgecrewio/checkov-action/issues/170)
> and [checkov#5866](https://github.com/bridgecrewio/checkov/issues/5866).
### pip install: VERSION-PINNED (Accepted Risk) ⚠️
Scorecard flags `pip install package==version` because it prefers hash pinning.
The remaining pip commands use exact version pinning.
**Flagged commands and their status:**
| File | Line | Command | Status |
|------|------|---------|--------|
| Dockerfile | 49-50 | `pip3 install pip==26.0` `pip install pdm==2.26.2...` | Version-pinned |
| Dockerfile | 194 | `pip3 install pip==26.0` | Version-pinned |
| check-env-vars.yml | 36 | `pip install loguru==0.7.3 sqlalchemy==2.0.36...` | Version-pinned |
| checkov.yml | 33 | `pip install checkov==3.2.499` | Version-pinned |
| fuzz.yml | 51-52 | `pip install pip==25.0` `pip install pdm==2.26.2` | Version-pinned |
| mypy-type-check.yml | 39,47-48 | `pip install pdm==2.26.2 mypy==1.14.1...` | Version-pinned |
| publish.yml | 139,328 | `pip install pdm==2.26.2` `pip install wheel==0.46.2` | Version-pinned |
| puppeteer-e2e-tests.yml | 65 | `pip install -e .` | Local package |
| responsive-ui-tests-enhanced.yml | 97-98 | `pip install pip==25.0` `pip install -e .` | Version-pinned + local (Alert #5688) |
| semgrep.yml | 36 | `pip install semgrep==1.87.0` | Version-pinned |
| update-precommit-hooks.yml | 35-38 | `pip install pip==25.0` `pip install pre-commit-update==0.6.1` | **Hash-pinned** |
| validate-image-pinning.yml | 67 | `pip install pyyaml==6.0.2` | Version-pinned |
| backwards-compatibility.yml | 66-68 | `pip install --upgrade pip` `pip install pytest` `pip install -e .` | Intentionally unpinned |
| backwards-compatibility.yml | 106-107 | `pip install --upgrade pip` `pip install pytest` | Intentionally unpinned |
| backwards-compatibility.yml | 154-155 | `pip install --upgrade pip` `pip install "local-deep-research==..."` | Intentionally unpinned |
| backwards-compatibility.yml | 169 | `pip install -e .` | Local package |
> **Note:** `backwards-compatibility.yml` intentionally uses unpinned pip commands
> because it tests compatibility with prior PyPI releases of local-deep-research.
> Pinning these commands would defeat the purpose of the compatibility tests.
**Why we don't use hash pinning:**
1. **Platform-specific hashes**: pip package hashes vary by Python version, OS, and architecture.
A single hash won't work across different CI runners.
2. **Maintenance burden**: Every version update requires regenerating hashes for all platforms.
3. **Marginal security benefit**: These are dev/CI tools running in hardened CI environments
(step-security/harden-runner) with egress auditing. Supply chain attacks on PyPI packages
are mitigated by version pinning and short execution windows.
4. **Industry practice**: Version pinning (`==`) is the standard for CI tool installation.
Hash pinning is typically reserved for production dependencies.
5. **Local packages**: `pip install -e .` installs the local source code and cannot be hash-pinned.
6. **Bootstrap commands**: `pip install pip==X.Y.Z` cannot be hash-pinned because pip is the
tool performing the verification — it cannot verify its own integrity during a self-upgrade.
### Specific OSSF Scorecard Alerts
| Alert | File | Lines | Description | Status |
|-------|------|-------|-------------|--------|
| #5688 | responsive-ui-tests-enhanced.yml | 97-98 | `pip install pip==25.0` (bootstrap) and `pip install -e .` (local source) | Accepted limitation |
### npm Commands: MOSTLY COMPLIANT (21/24) ⚠️
The 3 "unpinned" npm commands are operational commands, not package installations:
| File | Line | Command | Reason Not Pinned |
|------|------|---------|-------------------|
| npm-audit.yml | 56 | `npm i --package-lock-only` | Generates lockfile only |
| npm-audit.yml | 72 | `npm i --package-lock-only` | Generates lockfile only |
| update-npm-dependencies.yml | 74 | `npm update` | Intentionally updates to latest |
These commands don't install packages directly - they either generate lockfiles
or intentionally update packages. They cannot and should not be "pinned".
### downloadThenRun: FALSE POSITIVE ⚠️
**Flagged:** `examples/elasticsearch/test_elasticsearch.sh:60`
```bash
curl -s http://localhost:9200 | python3 -m json.tool | head -10
```
**Why it's a false positive:**
- This fetches JSON from localhost:9200 (local Elasticsearch)
- Pipes to `python3 -m json.tool` (stdlib JSON formatter)
- Shows first 10 lines of pretty-printed output
This is NOT downloading and running a remote script. It's formatting local JSON output.
The scorecard pattern-matches `curl | python` as potentially dangerous, but this
is a safe operation on localhost data.
**OSSF Scorecard Alert:** #4411
### APT Packages: INTENTIONALLY UNPINNED ⚠️
**Files affected:** `publish.yml`, `e2e-research-test.yml`, `responsive-ui-tests-enhanced.yml`, `Dockerfile`
| File | Packages | Runner/Base |
|------|----------|-------------|
| publish.yml | libsqlcipher-dev, patchelf | ubuntu-22.04 |
| e2e-research-test.yml | jq | ubuntu-22.04 |
| responsive-ui-tests-enhanced.yml | wget, gnupg, ca-certificates, fonts-liberation, etc. | ubuntu-latest |
| Dockerfile | curl, git, build-essential, etc. | python:3.13.9-slim@sha256:... |
**Rationale for NOT pinning APT packages:**
1. **Version availability**: Old APT package versions are removed from Ubuntu archives after 6-12 months.
Pinning to `package=1.2.3-1ubuntu1` causes builds to fail when that version is removed.
2. **Base image controls versions**: Docker base images are SHA-pinned, which deterministically controls
which APT package versions are available. The combination of `python:3.13.9-slim@sha256:326df678...`
and `apt-get install curl` produces the same result every time that base image is used.
3. **Runner stability**: GitHub workflow runners use pinned Ubuntu versions (e.g., `ubuntu-22.04`)
which provide consistent package versions throughout the runner's lifecycle.
4. **Version variation**: APT package version strings vary between Ubuntu releases and architectures,
making cross-platform pinning impractical.
5. **Industry consensus**: Security experts recommend pinning the base image/runner rather than
individual packages. Base image pinning provides stronger guarantees with lower maintenance burden.
**Mitigations in place:**
- ✅ Docker base images pinned to SHA256 digests (see Docker Images section above)
- ✅ GitHub runner versions pinned where practical (ubuntu-22.04)
- ✅ Dependabot configured to monitor for security updates
- ✅ Step-security/harden-runner audits all egress traffic
- ✅ Minimal package sets installed (only what's needed)
### Enforcement
We have automated verification for our pinning strategy:
- `.github/workflows/validate-image-pinning.yml` - Validates Docker image digests
- Pre-commit hooks verify action SHA pinning
- All pip install commands use explicit version specifiers (except `backwards-compatibility.yml` which intentionally tests with unpinned versions)
### Review Cadence
These decisions are reviewed quarterly to ensure they remain appropriate:
- **Next review:** Q2 2026
- **Owner:** Security team
## References
- [OSSF Scorecard Pinned-Dependencies Check](https://github.com/ossf/scorecard/blob/main/docs/checks.md#pinned-dependencies)
- [StepSecurity Harden Runner](https://github.com/step-security/harden-runner)
- [pip Hash Checking Mode](https://pip.pypa.io/en/stable/topics/secure-installs/#hash-checking-mode)
Scanned 5/31/2026
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!