Filing Guide

How to turn this assessment into GitHub epics and issues: the epic structure, the five parameters every issue must set, labels, and the Linear mirror.

This page turns the rest of the section into a backlog. It gives you the epic structure, an issue template, and the parameters every issue has to carry.

Start here, in this order

  1. File the open questions triage ticket first. One ticket, five lookups, an afternoon. Several answers change the shape of epics below, and two of them decide whether a finding is High or Critical. Filing epics before this is filing work you may have to resize.
  2. File the four Tier 1 fixes as standalone issues, not inside an epic. They are hours of work, they block nothing, and burying them in an epic is how they wait three weeks.
  3. Then file the nine root-cause epics below.

Doing 2 before 3 matters. The Tier 1 items are cheap and time-sensitive, and epic structure is a planning aid rather than a queue.

The epic structure: nine epics, one per root cause

The root causes page explains why this axis and not another. In short: 142 of the 184 findings map to nine causes, so nine epics carry three quarters of the work, and closing a cause stops the class from regenerating.

Epic Root cause Findings C H
SEC-E1 No shared security component 32 0 1
SEC-E2 Hasura admin secret is the only service identity 8 0 3
SEC-E3 Deny-by-default was never adopted 29 2 7
SEC-E4 Boundaries are values inside a request 20 0 3
SEC-E5 No artifact reconciles what exists 11 0 1
SEC-E6 A declared control does not bind 13 0 0
SEC-E7 No enforcement point between change and production 14 0 2
SEC-E8 Nothing observes a security decision 2 0 0
SEC-E9 Credential material has no lifecycle owner 13 0 5

Plus four from the personal data inventory, which are a different axis over the same estate and should not be folded into the nine:

Epic Subject
SEC-P1 Retention exists
SEC-P2 Get personal data out of git
SEC-P3 Coarsen by default on the location paths
SEC-P4 Assert encryption at rest where it matters

42 findings map to no cause. Do not invent a tenth epic for them. File the ones that matter individually against their component, and let the rest sit in the register until someone has capacity.

Every issue sets five parameters at creation

Not as a follow-up. An issue missing any of these is invisible to whoever is running the board.

  1. Labels: every applicable label. At minimum security, the component label, and a priority:*.
  2. Type: the org issue type, Feature, Bug or Task. Set it through the GraphQL updateIssueIssueType mutation using node IDs from organization { issueTypes }. The REST type field silently no-ops, so a PATCH looks like it worked and does nothing.
  3. Project: add it to the owning org project board and set the Status field (Backlog / Ready / In Progress / In Review / Done). The session gh token usually lacks project scope; use a PAT with it.
  4. Milestone: the matching repo milestone.
  5. Relationship: parent epic via the GraphQL addSubIssue mutation with the GraphQL-Features: sub_issues header, or cross-references in the body where a true parent does not exist.

Labels to use

Reuse the estate’s harmonised set rather than inventing new ones: security, c2pa, mobile, ios, android, enrollment-api, ca, docs, infrastructure, disaster-recovery.

Add one assessment-specific label so the whole body of work stays findable after the epics close: assessment-2026-08.

For severity, use priority:* and map it: Critical to your highest priority, High to the next. Do not create severity labels that duplicate priority, because the two drift and then nobody knows which one the board sorts on.

The Linear mirror

Where work is mirrored into Linear, keep both in lockstep with these equivalents:

GitHub Linear
Labels Team labels
Type (Feature / Bug / Task) Type-labels feature / bug / Task
Project board + Status field Native Project + workflow State
Milestone Project Milestones, using the same names as the GitHub milestones
Sub-issue parentId
Cross-dependency issueRelationCreate with type blocks / related / duplicate
priority:* labels Native priority (1 = urgent … 4 = low)
Assignee Assignee

Reference the GitHub issue URL in the Linear description and the Linear URL in the GitHub issue.

Issue template

Copy this. It is short on purpose.

## Finding

MGR-H11 (High, Verified). Twelve tables grant role user an unfiltered
read, exposing every account's email and the coordinates and media of
unpublished sightings.

Reference: /docs/security/platform-assessment-2026-08/critical-and-high-findings/#mgr-h11

## Location

hasura/metadata/databases/default/tables/public_users.yaml:66
(plus 11 further tables, listed in the finding)

## What is wrong

<two or three sentences, in your own words, from the finding page>

## What to change

<the "what to change" list from the finding page, as checkboxes>

## Done when

<a statement someone can check, not "the fix is merged">

## Blocks / blocked by

Blocked by: #<mobile client query change>
Related: MGR-H07 (same permission class, same repo)

On “Done when”

Write it as something a reviewer can verify by looking at the system, not at the diff. “The permission block no longer has an empty filter” is checkable. “Fixed the over-grant” is not. Several of these findings have a build assertion as their durable fix, and for those the done-when is that the assertion fails on a deliberately reintroduced bad value. If nobody has seen the check go red, nobody knows it works.

Ordering constraints that will bite you

These are dependencies between findings, and they are the reason a naive severity-sorted board goes wrong.

Two remediation steps in the source are wrong

The assessment’s own handling notes flag two remediation steps that would cost you time if followed literally:

  • One names a DNS zone that does not exist.
  • One tells you a firewall change will not strand a deploy, when it will.

Neither is identified more precisely than that in the notes available. Treat any remediation step that names a specific DNS zone, or that asserts a firewall or WAF change is deploy-safe, as needing its own verification before you run it. That is a five-minute check against your own accounts and it is cheaper than the alternative.

24 evidence rows across the four committed-credential findings carry a proof URL that does not resolve. If you are chasing a citation on MGR-H01, MGR-H06, MGR-H24 or the fourth in that class and the link is dead, that is a known defect in the source, not evidence that the finding is wrong. Verify against the repository directly.

What not to put in an issue

The handling rules from the section index apply to every ticket you file:

  • No credential values. Describe a secret by what it is and where it lives. Never paste one, including into a private issue.
  • No personal data rows. Reference a table and a column list; do not paste records.
  • No quoting the total outside the team. The 184 is provisional and under revision.
  • Do not attribute the work externally. The provenance constraints on the source document have not changed just because the work moved into your tracker.