> For the complete documentation index, see [llms.txt](https://hasithz.gitbook.io/hasith-abayakoon/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hasithz.gitbook.io/hasith-abayakoon/selected-work/projects/secure-scanning-components.md).

# Security scanning components for a shared CI/CD platform

Snyk and SonarQube scanning components, the Python modules behind them, and the release automation that ships them to a shared GitLab CI/CD platform.

**Pearson, November 2025 – present.** Platform engineering and security tooling. Status as of October 2026: in use in the shared platform pipeline and under active development.

## Problem

Pearson's delivery teams build and deploy through one shared GitLab CI/CD platform: a set of pipeline templates, a Python automation framework, and a family of Docker build images. Security scanning had grown up around that platform piecemeal. Container images were scanned by Twistlock, dynamic scanning ran through Insight AppSec, infrastructure code through InsightCloudSec, and Snyk scans were written by hand into individual image pipelines. Several of those services were being retired, scan jobs failed in ways that were hard to diagnose, and a few credentials and SSH keys had been baked into images and configuration files along the way.

Teams needed one way to get code (SAST), dependency (SCA), container, and code-quality scanning in any pipeline, with results they could read and a pass/fail behaviour they could reason about.

## My role

I am the primary author of the Snyk module and the SonarQube module in the DX Secure group, and of the shared Python modules they depend on. That covers the module code, the GitLab CI/CD components, the release automation, the documentation, and the integration of both components into the platform pipeline.

Integration changes to the shared pipeline repositories were delivered as merge requests reviewed by the platform's maintainers. The earlier Snyk onboarding and scanner-retirement work (2024–2026) was part of the platform team's backlog. Consumer-side adoption was done with the owning teams in their repositories.

## Constraints

* **Consumers include a component, not code.** Everything a team can change has to be exposed as component inputs or CI variables, and defaults have to be safe for a pipeline that has never seen the component before.
* **One component, many project types.** The same scan has to work for Linux and Windows images, Node.js, Go, Python, .NET, Lambda, ECS, and micro-frontend repositories, and SonarQube coverage had to be measured for twelve languages.
* **No secrets in images or repositories.** Scanner tokens had to come from AWS Secrets Manager at run time, and CI output could never echo them.
* **SonarQube Community Edition.** Language detection, analyzer availability, and failure modes differ from the commercial editions and had to be reported honestly.
* **Multi-project repositories** and repositories with no coverage data had to scan without false failures.
* **Release discipline.** Consumers pin a version, so every release has to be publishable, idempotent, and compatible with the images it was built against, while push rules constrain what CI may commit.
* **Legacy scanners were being retired in parallel**, so the new components had to land without leaving pipelines unscanned.

## Decisions

1. **A Python module with thin CI components, not shell in YAML.** The Snyk module was rebuilt on a hexagonal architecture with a scan orchestrator at the centre and adapters at the edges, and a full command-line interface so the component and the CLI share one code path. The cost was more structure up front. The payoff was testable code and a pattern the SonarQube module could reuse six months later.
2. **Shared modules for shared concerns.** AWS access, GitLab API access, and multi-source configuration loading became separate packages (aws-module, gitlab-module, config-read) with their own pipelines and release jobs, so neither scanner module carries that code.
3. **Policy decides the outcome, not the scan.** A found vulnerability or a red Quality Gate is a policy result for the consuming team. The scanner reports it faithfully and exits with a real code, and the component lets the team choose soft-fail or hard-fail. Defaults are soft-fail, so adopting the component never blocks delivery on day one.
4. **Consolidate where it simplifies, separate where it does not.** SAST and dependency scanning were merged into one snyk-scan component; container scanning stayed a separate component because it runs at a different point in the pipeline.
5. **Secrets and diagnostics hygiene as design rules.** Tokens are read from Secrets Manager through aws-module, CI stops echoing them, and diagnostics go to stderr so stdout stays machine-readable for the next job.
6. **Release automation that cannot lie.** Versions derive from the git tag, patch releases cut automatically on pushes to main, publishing is idempotent, tags are plain X.Y.Z, and templates default to the images of their own release. Main never mints a release it does not publish.
7. **Design options as draft merge requests.** For how consumers pin or float the component version, I wrote four alternative designs as drafts, from an exact override with a major-version guard to a floating channel through the CI/CD Catalog, so reviewers compared concrete options rather than a single proposal.

## Evidence

```mermaid
flowchart LR
  subgraph consumer [Consumer repository pipeline]
    inc[include snyk-scan, snyk-container, or sonarqube component]
  end
  subgraph catalog [CI/CD Catalog, releases X.Y.Z]
    comp[Component templates<br/>pinned to their release images]
  end
  subgraph modules [Python modules]
    snyk[snyk-module<br/>orchestrator, reports, failure policy]
    sonar[sonarqube-module<br/>scan, quality policy, coverage]
    shared[aws-module, gitlab-module, config-read]
  end
  inc --> comp
  comp --> snyk
  comp --> sonar
  snyk --> shared
  sonar --> shared
  shared --> sm[(AWS Secrets Manager)]
  shared --> gl[GitLab API]
  snyk --> r1[HTML report with source lines<br/>Snyk dashboard via monitor<br/>exit code per failure policy]
  sonar --> r2[SonarQube server<br/>Quality Gate as a policy result]
```

* **Release trail.** Snyk module releases 2.0.0 (May 2026), 3.0.x and 3.1.0 (July–August 2026), and 4.0.0 (October 2026). SonarQube module from base infrastructure (July 2026) through 0.2.x to 0.5.3 (September 2026), with the platform pipeline moving to the 4.0 Snyk components and the SonarQube component in October 2026.
* **Validation.** The container scanning component was exercised across fourteen sample applications before the platform integration merged.
* **Documentation.** The SonarQube module ships a documentation tree and Docusaurus site with docs CI jobs; the Snyk refactor series included templates and migration docs.
* **Public record.** Every change is listed in the [merge-request activity](/hasith-abayakoon/experience/pearson-contributions/gitlab-mrs.md) and summarised in the [role overview](/hasith-abayakoon/experience/pearson-contributions/role-overview.md). The repositories themselves require organisational access.

## Outcome

* Both components are in the shared platform pipeline and are the platform's supported route to Snyk and SonarQube scanning.
* Four legacy scanners (Twistlock, Insight AppSec, InsightCloudSec, Scorecard) have been removed from the platform's pipelines and framework.
* Hard-coded tokens and baked-in SSH keys were removed from the DevOps configuration and the Docker images.
* Consumer teams can switch a scan from soft-fail to hard-fail per repository, and multi-project repositories scan without false failures.

No adoption counts or vulnerability figures are published here; the GitLab record lists each change individually.

[Back to selected work](/hasith-abayakoon/selected-work/projects.md)
