get2knowio / get2knowio/hangar
Add a `dependency_graph` check: verify the Dependency graph feature is enabled (not just the dependency-review workflow)
- Dominant language
- Python
- Stars
- 0
- Forks
- 0
- Avg merge
- 22h 46m
- Merged PRs (30d)
- 16
Description
## Summary
Hangar has **no check that verifies the GitHub *Dependency graph* feature is enabled** on a repo. The two adjacent Supply-chain checks each cover something else:
- `dep_review` ("Dependency review enabled") passes purely on the **presence of the `dependency-review-action`** in `.github/workflows/*` (see `_workflows_group` in `providers/github/detection.py`). It never checks whether the graph the action depends on is actually on.
- `dependabot_alerts` reads the `vulnerability-alerts` endpoint (204/404) — a **different** feature, and (see below) not a reliable proxy for the graph being enabled.
The result is a blind spot: a repo can show **`dep_review` ✅ and `dependabot_alerts` ✅** while the `dependency-review` workflow **fails at runtime** with *"Dependency review is not supported on this repository. Please ensure that Dependency graph is enabled."*
## Evidence this is real
- **`get2knowio/remo`**: had the `dependency-review-action` wired up (so `dep_review` would pass) and `vulnerability-alerts` returned `204` (so `dependabot_alerts` would pass), yet every PR's `dependency-review` check failed because the Dependency graph feature was disabled. Enabling *Settings → Advanced Security → Dependency graph* was the only fix.
- **Hangar's own #62** already documents the same symptom on the hangar repo:
> **Enable Dependency graph** … The workflow currently fails with "Dependency review is not supported on this repository." … Hangar's `dep_review` check passes on the workflow's presence regardless, but the workflow itself only goes green once this is on.
#62 tracks *manually remediating hangar's own repo*; this issue is the **product gap** — teaching Hangar to *detect* the state across every managed repo.
## Proposed change — add a `dependency_graph` check
**1. Check definition** — `domain/checks/supply_chain.py`, group `Supply chain`:
```python
Check(
id="dependency_graph", label="Dependency graph enabled", group=_G,
tier=RemediationTier.link, required_capabilities=caps_for_tier(RemediationTier.link),
evidence_fail="Dependency graph feature disabled (dependency-review cannot run)",
doc_url="https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-the-dependency-graph",
),
```
It complements `dep_review`: `dep_review` = the action is wired up; `dependency_graph` = the feature the action needs is actually on.
**2. Detection** — `providers/github/detection.py`, a new group gathered alongside `_dependabot_alerts_group`. The graph's enablement is **not** cleanly exposed as a per-repo `security_and_analysis` field, so probe an endpoint that only works when it's on. Two options (implementer's call, matching Hangar's "honest `unknown`, never fabricate a pass" rule):
- **SBOM** `GET /repos/{owner}/{repo}/dependency-graph/sbom` — `200` → pass; `403`/undeterminable → `unknown`. Caveat: a fresh-enabled repo can `404` briefly before the first scan, so treat bare `404` cautiously.
- **Compare** `GET /repos/{owner}/{repo}/dependency-graph/compare/{base}...{head}` — this is what the `dependency-review` action itself calls; it returns a distinct "not supported" error when the graph is off, which is the most faithful signal. Needs two refs (e.g. `default_branch~1...default_branch`).
⚠️ **Do not** piggyback on the `dependabot_alerts` (`vulnerability-alerts`) signal: on `remo` that endpoint returned `204` (alerts "enabled") while the graph was genuinely off, so it is not a reliable proxy.
**3. Remediation tier = `link`, not `settings_patch`.** There is **no reliable REST API to auto-enable the graph per repo**:
- `PATCH /repos/{o}/{r}` with `security_and_analysis.dependency_graph` is **silently ignored** (verified — no effect).
- `POST /orgs/{org}/dependency_graph/enable_all` returns `204` but is **org-scoped** and effectively a **no-op on free-plan orgs** (verified on `get2knowio`).
So it must deep-link the operator, consistent with how `secret_scanning`/`code_scanning` are handled. Add to `adapter.py`'s `deep_link`:
```python
"dependency_graph": "/settings/security_analysis",
```
(Per `_apply_settings`' own contract, a `settings_patch` that can't converge "would be a silent no-op that falsely reports success" — this check must stay `link`.)
**4. Gitea** — `providers/gitea/detection.py`: no dependency-graph feature, so mark `dependency_graph` as `unknown`/unsupported alongside `dependabot_alerts`.
**5. Tests / seed** — extend the GitHub detection tests (enabled → pass, disabled → fail, 403/no-capability → `unknown`) and add the id to any seed fixtures.
## Acceptance criteria
- [ ] `dependency_graph` check appears in the Supply-chain group.
- [ ] Detects enabled (pass) / disabled (fail) / undeterminable (unknown) on GitHub, capability-gated.
- [ ] Gitea reports it `unknown` (unsupported).
- [ ] Remediation deep-links to `…/settings/security_analysis` (link tier; no false-success settings_patch).
- [ ] Running Hangar against `get2knowio/remo` (graph now enabled) passes, and would have **failed** before it was enabled — i.e. it catches the exact state `dep_review` misses.
_Related: #62 (manual remediation checklist that first surfaced this on the hangar repo)._
Contributor guide
Research direction
Start with the Supply chain checks in domain/checks/supply_chain.py and the adjacent detection logic in providers/github/detection.py. Review the existing GitHub detection tests, providers/gitea/detection.py, and adapter.py's deep_link handling. Done means the check reports enabled, disabled, or unknown appropriately, remains unsupported on Gitea, and links to settings/security_analysis without a false-success patch.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- github, python
- Domain
- api, backend, devtools
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100