Engineering Discovery: first-class vulnerabilities
Full context: &634 (closed)
Product discovery (high-level choices): https://gitlab.com/gitlab-org/gitlab-ee/issues/8493
This iteration is about the technical engineering discovery to understand how we can implement first-class vulnerabilities in our current architecture.
Notes from conversation about this topic at Contribute.
NOTE: much of this issue has refocused on UX and Product discovery as many points needed to be revisited. See discussion below for details on initial JTBD, interactions, and direction.
Development log
Status
-
Jobs To Be Done defined -
Data model defined -
Implementation steps finalized
Decisions
Terminology
Finding
Something with the potential to be vulnerable. This is a subgroup of the existing Vulnerability Occurrences in a pre-merge state (within Merge Terminology). Findings are not persisted in the database.
Priority Finding
Something with the potential to be vulnerable. This is a subgroup of the existing Vulnerability Occurrences in a post-merge state (within default branches). Priority Finding is persisted in the database and maps to a record in vulnerability_occurrences DB table. A Priority Finding is an "occurrence" that has not been confirmed (and didn't spawn a Vulnerability entity yet) but it has become a part of the production-deployed codebase. It can be in either confirmed or unconfirmed state. A confirmed Priority Finding is the one that has a Vulnerability referencing it.
Vulnerability
A confirmed entity that represents a vulnerability within the codebase. This is a result of the "promotion" of a Priority Finding. A Vulnerability is either in a state of open (some unresolved Findings exist) or resolved (no more** unresolved Findings exist).
**A vulnerability MUST have occurrences. Even externally-sourced weaknesses such as HackerOne reports require triaging, so they will first appear as weakness and require promotion to a Vulnerability.
Triaging
Triaging is the act of promoting a Finding to a Vulnerability.
This behavior will be introduced from a functional perspective within #11205 (closed).
Triaging is not something that is meant to occur through the MR widget and primarily through dashboard views, hence we say Priority Finding promotion, not just Finding promotion. This is an important distinction. The existing MR procedures of dismissal are preserved as feedback is directly associated with vulnerability_occurrences.
When interacting with Vulnerabilities, a dismissal would result in creating cascading feedback for all associated vulnerability_occurrences. This way we preserve the existing DB relationships, maintain backward compatibility, and allow more granular interactions; i.e. for a Vulnerability w/ 2 Findings we dismiss 1 Finding (within test directory) but create an Issue to address 2nd Finding (within src directory).
Data Model
- Naming consideration:
vulnerability_occurrencesshould becomefindingsto more accurately capture the potential of a vulnerability, where "occurrences" implies a confirmation -
vulnerabilitieshave manyvulnerability_occurrences -
vulnerabilitieshave manyissuesthroughvulnerability_feedback(throughvulnerability_occurrences) - a
vulnerabilityis "resolved" when allfindingsare "resolved" - a
vulnerabilityonly has 2 states:openandclosed. All possibleclosedsub-states (i.e. "duplicate", "wont fix", "false positive") should be dismissals of thefinding -
vulnerabilities.severityandvulnerabilities.confidenceare aggregates of the associatedfindings, however they can be overridden by the user.- By default,
vulnerability.severityis the highest-severity of linked occurrences - By default,
vulnerability.confidenceis the lowest-confidence of linked occurrences
- By default,
-
vulnerability_feedbackremains associated withvulnerability_occurrences. To dismiss a vulnerability that has 2 findings, dismissal feedback is applied to all associated findings as a cascading transaction -
vulnerability_feedback.feedback_typeuniqueness constraint must be dropped in order to allow afindingto have multiple-associated issues -
we're not going to addfor the MVC, we're adding the report_type to the Vulnerabilities (details here).report_typeattribute toVulnerabilitiesbecause theVulnerabilityhas a higher level of abstraction than report and may combine findings from different report types
Interactions
-
Vulnerabilityis an epic-like object -
Vulnerabilityis a project-scoped object (not a group-scoped, like Epics) - Naming consideration:
findingsare all occurrences. "Priority findings" are present within the default branch. - a 1st Class Vuln (
vulnerability) can be created from afinding - a 1st Class Vuln (
vulnerability) must be associated withfindings(a vulnerability cannot be created in isolation as findings contain the specifics of the vulnerability; i.e. location) - Externally-sourced
findings(i.e. HackerOne reports) will always be created asfindingsto allow triaging and potential promotion to vulnerabilities (vulnerability_occurrences.scanner.name == "hackerone") - a
findingmust be promoted to avulnerabilityto allow otherfindingsto be associated with that vulnerability (bulk-associating findings to a vulnerability on creation will not be supported, but this could be an implementation detail) - a
findingcan be linked to an existingvulnerability(and only one) - a
findingcan be disassociated with avulnerability(unless it's the last-associatedfinding) -
vulnerabilitiesshould be considered "resolved" when all findings are "resolved".- This will not be an automatic action initially as pipeline-completion (when we detect that findings no longer exist) cannot accurately be tied to a production deployment (to which a vulnerability SLA should be associated).
- Externally-sourced findings must be manually resolved and will never be auto-closed
- a
Vulnerabilityis created confidential similarly to confidential Issues
UX
- a board with mockups, flows, critical assumptions, and open questions
Backend MVC Implementation Plan
- Rename existing
Vulnerabilities APItoVulnerability Findings API - Create
vulnerabilitiestable (see schema below) - Create
Vulnerabilities APIwith the following capabilities
Get a list of project's vulnerabilities-
Create vulnerability from finding(the old term is "occurrence") -
removedDelete vulnerability(and disassociate from occurrence) - Update vulnerability status:
-
Dismiss vulnerability(and dismiss all associated occurrences) -
Resolve vulnerability(and resolve all associated occurrences)
-
- [Optional, depending on capacity]
Associate finding to (existing) vulnerability
Frontend MVC Implementation Plan
- Rename Security Dashboard
Vulnerability ListtoFinding List - Add secondary tab for
VulnerabilitiesGET /api/v4/projects/:id/vulnerabilitiesGET /api/v4/vulnerabilities/:id
- Add interaction to "promote" a
Findingto aVulnerabilityPOST /api/v4/vulnerabilities { finding_id: <finding_id> }
-
Add interaction to delete avulnerability-
removed because is considered unnecessaryDELETE /api/v4/projects/4/vulnerabilities/:primary_identifier_id-:location_fingerprint-:scanner_id
-
- Add interaction to mark a
vulnerabilityas resolvedPOST /api/v4/vulnerabilities/:id/resolve
- Add interaction to mark a
vulnerabilityas dismissedPOST /api/v4/vulnerabilities/:id/dismiss
- [Optional, depending on capacity] Add interaction to associate finding with existing
vulnerability-
POST /api/v4/vulnerabilities/:id/attach { vulnerability_id: <vulnerability_id> }: post-MVC, extracted into #34531 (closed)
-
Schema
vulnerabilities
create_table "vulnerabilities" do |t|
# epic-like (or issue-like) attributes
t.bigint "milestone_id"
t.bigint "epic_id"
t.bigint "project_id", null: false
t.bigint "author_id", null: false
t.bigint "updated_by_id"
t.bigint "last_edited_by_id"
t.date "start_date"
t.date "due_date"
t.datetime "last_edited_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "title", null: false
t.string "title_html", null: false
t.text "description"
t.text "description_html"
t.bigint "start_date_sourcing_milestone_id"
t.bigint "due_date_sourcing_milestone_id"
t.integer "state", limit: 2, default: 1, null: false
t.bigint "closed_by_id"
t.datetime "closed_at"
t.integer "report_type", limit: 2, default: 0, null: false
# vulnerability-like attributes
t.integer "severity", limit: 2, null: false # auto-calculated as highest-severity finding, but overridable
t.boolean "severity_overridden", default: false
t.integer "confidence", limit: 2, null: false # auto-calculated as lowest-confidence finding, but overridable
t.boolean "confidence_overridden", default: false
end
add_foreign_key :vulnerabilities, :projects
add_foreign_key :vulnerabilities, :milestones
add_foreign_key :vulnerabilities, :epics
add_foreign_key :vulnerabilities, :users, :author_id
add_foreign_key :vulnerabilities, :users, :updated_by_id
add_foreign_key :vulnerabilities, :users, :last_edited_by_id
add_foreign_key :vulnerabilities, :users, :closed_by_id
* iid (project-scoped internal ID) was removed from the initial schema by the maintainer recommendation
vulnerability_occurrences
add_reference :vulnerability_occurrences, :vulnerabilities, :bigint, index: true, foreign_key: true