SDD — Dependency and Vulnerability Reports

For how to use the reports, see Dependency and Vulnerability Reports.

Components

Jira / Confluence: Apps → Dependency reports ─┐ GitHub API dispatch
GitHub: Actions → Dependency report ──────────┤
                                              ▼
   service repo: .github/workflows/dependency-report.yml   (button; fixed Harbor image)
                                              │ uses …@main, secrets: inherit
                                              ▼
   ft-internal-tools: .github/workflows/dependency-report-callable.yml
     check job   tag exists in Harbor (seconds; lists newest tags on a miss)
     report job  syft → SBOM · Trivy → vulnerabilities
                 dependency-report.py build   → xlsx, pdf, page, meta
                 dependency-report.py publish → Confluence pages + attachments
                 dependency-report.py jira-comment (optional)
File Repository Role

.github/workflows/dependency-report.yml

each service

The button. Inputs are tag, severity and jira_issue; the Harbor project and image are fixed. It calls the shared workflow.

.github/workflows/dependency-report-callable.yml

ft-internal-tools

Validates the inputs, checks the tag in Harbor, runs syft and Trivy, then builds, publishes and comments.

ft-tools/dependency-report/dependency-report.py

ft-internal-tools

Subcommands build, publish and jira-comment. Uses the Python standard library, openpyxl and reportlab.

ft-tools/jira-dependency-report/

ft-internal-tools

The Forge app dependency-reports, a page in Jira and in Confluence. It lists the Harbor tags, dispatches the run and follows it.

The workflow runs on rd-runner inside a ubuntu:24.04 container, so nothing is installed on the runner. The tool versions are pinned in the workflow: syft v1.52.0 and Trivy v0.74.0. Trivy is the scanner Harbor runs. Runs for the same image repository are queued and never cancelled.

Confluence layout

Everything lives in space PSEC, under the parent page 511082755.

Dependency and Vulnerability Reports   index            rebuilt on every publish
└── Dependencies — <service>           service          versions + last 3 scans
    └── Dependencies — <service> <tag> version          every scan of the tag
        └── … <tag> · <date> UTC       scan             report + attachments, never rewritten
  • Every level is rebuilt from the content properties of the level below: the scan page’s ft-dependency-report property and the version page’s ft-dependency-report-version property. Nothing is parsed back out of rendered HTML. A page that someone deletes drops out on the next publish.

  • The Changes block compares the scan with the previous scan of the same tag. It compares the counts, the Critical and High findings (stored per scan, up to 400) and the image digest.

  • All pages are full-width. They carry the labels dependency-report, dependency-report-<kind> and dependency-report-<service>.

  • After publishing, the run leaves a Confluence report annotation with the URL of the scan page. The Forge app reads it to link the report directly: Confluence search indexes new pages only minutes later.

Setup

Table 1. GitHub, organisation level (visible to the service repositories)
Kind Name Value

Variable

CONFLUENCE_SPACE_KEY

PSEC

Variable

CONFLUENCE_PARENT_ID

511082755

Secret

JIRA_BASE_URL, JIRA_EMAIL, JIRA_API_TOKEN

Account that writes pages and comments. The same token works for Jira and Confluence.

Secret

CONFLUENCE_USER, CONFLUENCE_TOKEN

Optional. Set only if pages should be written by another account.

Secret

HARBOR_USERNAME, HARBOR_PASSWORD, CI_CD_TOKEN

Already present in the repositories. Used to pull the image and to fetch the generator.

In ft-internal-tools, set Settings → Actions → General → Access to "accessible from repositories in the organization", and make sure CI_CD_TOKEN can read ft-internal-tools.

Forge app

The app has three encrypted Forge variables in the production environment: HARBOR_USERNAME, HARBOR_PASSWORD and GITHUB_TOKEN, a token with Actions read/write on the service repositories.

  1. Deploy with forge deploy -e production.

  2. A site admin installs the app in Jira and/or Confluence, from the installation link in the developer console.

Adding a service

  1. Copy a dependency-report.yml button into the service repository. Set its Harbor project and image.

  2. Add a line to SERVICES in ft-tools/jira-dependency-report/src/core.js. The line has the label, the repository, the default branch, the Harbor project and the image.

  3. Run forge deploy -e production.

Decisions

Decision Chosen Rejected

Trigger

On demand: the button in GitHub or the Forge page

Automatic on every image build (decision 2026-09-25).

Shared logic

One workflow and one script in ft-internal-tools, with thin buttons in the services

Keeping the shared code in one of the services it serves; four copies that drift apart.

Scanner

syft for the inventory and SBOM, Trivy for vulnerabilities

grype: a different database and GHSA IDs, and its numbers did not match Harbor. Harbor’s export: …/additions/sbom rejects plain images (goharbor/harbor#22315).

History

One page per scan, never overwritten

One page per tag, overwritten on every rerun (the first version, replaced on 2026-09-26).

Storage

Confluence Cloud

GitHub artifacts: the organisation quota is exhausted.

Failure modes

Failure Behaviour

Tag not in Harbor

The check job fails within seconds and lists the newest tags. Nothing is pulled.

Missing Confluence configuration

Publishing fails and names every missing setting.

Confluence or Jira HTTP error

The run fails and reports the method, the path and the status.

Publishing only partly done

Rerun it: publishing is idempotent.

Jira comment fails

The report is already published; the run turns red so that the requester sees it.

Planned

A report for every service at sprint start, with a Jira ticket and an e-mail when Critical or High findings are present. Five questions are still open:

  • which tag to scan;

  • whether the threshold counts only fixable findings;

  • who receives the e-mail;

  • which Jira project the tickets go to;

  • which board’s sprint triggers the run.

The full draft is in ft-tools/dependency-report/README.md, section 10.