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 |
|---|---|---|
|
each service |
The button. Inputs are |
|
ft-internal-tools |
Validates the inputs, checks the tag in Harbor, runs syft and Trivy, then builds, publishes and comments. |
|
ft-internal-tools |
Subcommands |
|
ft-internal-tools |
The Forge app |
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-reportproperty and the version page’sft-dependency-report-versionproperty. 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>anddependency-report-<service>. -
After publishing, the run leaves a
Confluence reportannotation 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
| Kind | Name | Value |
|---|---|---|
Variable |
|
|
Variable |
|
|
Secret |
|
Account that writes pages and comments. The same token works for Jira and Confluence. |
Secret |
|
Optional. Set only if pages should be written by another account. |
Secret |
|
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.
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.
-
Deploy with
forge deploy -e production. -
A site admin installs the app in Jira and/or Confluence, from the installation link in the developer console.
Adding a service
-
Copy a
dependency-report.ymlbutton into the service repository. Set its Harbor project and image. -
Add a line to
SERVICESinft-tools/jira-dependency-report/src/core.js. The line has the label, the repository, the default branch, the Harbor project and the image. -
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: |
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 |
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.