microsoft / microsoft/hve-core

feat(workflows): close code-scanning issues whose alerts are no longer open

Open
#2,763 0 comments 0 reactions 1 assignee Claimed by @jkim323 View on GitHub
enhancement priority-3 security tech-debt workflows
Dominant language
Python
Stars
1.5k
Forks
301
Avg merge
3d 3h
Merged PRs (30d)
92

Description

## Summary

`create-gh-code-scanning-issues.yml` can create and update alert-tracking issues, but it has no path that closes one. When an alert is fixed or dismissed, its tracking issue stays open indefinitely and must be closed by hand.

Eight automated issues accumulated this way and were closed manually on 2026-08-23: #2725, #2652, #2462, #2460, #1499, #1500, #1502, #2461.

## Why the workflow cannot close anything today

`Get-CodeScanningAlerts.ps1` fetches with `state=open`, so `alerts.json` contains only live alerts. Every action in the workflow - create, edit, comment - sits inside a single loop over that file:

```bash
done < <(jq -c '.[]' alerts.json)
```

A per-item loop can only act on rules that are *present*. A fixed or dismissed alert simply disappears from the input, so the workflow never observes its absence. Closing requires a set difference over what is missing, which this shape cannot express. The gap is structural, not a missing branch in the loop body.

## Proposed fix

Add a reconciliation step after the loop:

1. Collect the rule IDs present in `alerts.json`.
2. List open issues whose body contains the `automation:security-scan:` marker.
3. Parse each issue's rule ID from its marker comment.
4. Close issues whose rule ID is absent from the live set, with a comment naming the run that observed the alert as no longer open.

### The guard is the hard part

An unguarded reconciliation pass is a mass mutation with a fail-open failure mode: any run where `alerts.json` is empty, truncated, or malformed would close every open alert issue at once.

Emptiness cannot be the guard - zero open alerts is a legitimate and reachable state. The guard must be artifact *validity*: abort before closing anything unless the file parses as a JSON array.

Note that `needs: [gh-code-scanning]` does not cover this. It blocks on job failure, but not on a job that succeeds while producing a bad artifact. The serialization defect filed alongside this issue is exactly that case: the step succeeded and wrote syntactically valid JSON of the wrong shape. The artifact-upload step also carries `if: always()`, so artifact existence is not evidence that the scan succeeded.

## Two related defects in the same step

**Deduplication only sees open issues.** The lookup uses `--state open`. If an issue is closed while its alert is still open, the next run files a duplicate instead of reopening. Once a close path exists, reopen-if-closed becomes the correct behavior; otherwise the two mechanisms will fight.

**The update branch overwrites human edits.** `gh issue edit --title --body --add-label "automated,needs-triage"` rewrites the entire body weekly and re-adds `needs-triage` to issues someone has already triaged. The weekly delta already goes in the comment the step posts; the body rewrite should be dropped or confined to a generated region.

## Sequencing

The serialization fix should land first and separately. It is a one-word repair to a currently-red workflow and should not sit behind review of a mass-mutation feature.

## Acceptance criteria

- [ ] Issues whose alerts are no longer open are closed automatically, with a comment citing the observing run
- [ ] Reconciliation aborts without closing anything when `alerts.json` is missing or does not parse as a JSON array
- [ ] Zero open alerts is handled as a valid state, distinct from a malformed artifact
- [ ] Deduplication finds closed issues and reopens rather than duplicating
- [ ] Weekly updates no longer overwrite human-edited titles and bodies, and no longer re-add `needs-triage` unconditionally
- [ ] Reviewed and validated by a qualified human reviewer

## Related

* #1498 - analyses-error detection. Its deduplication criterion needs the same marker-based issue lookup this work introduces, so building it here lets #1498 reuse the primitive. Its open question about whether automated issue creation is the right response is also informed by the eight stale issues above: creation without closure produces backlog debt.
* #2762 - the serialization defect referenced above. Land it first and separately.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.