Securing the SDLC: A Zero-Trust Framework for Supply Chain Security
The Common Problem Every Polyglot Team Faces
If you've ever worked in a microservices environment with services written in multiple languages, you know the pain. One morning you wake up to a CVE alert for log4j. You start hunting. Which of your services use it? Which version? Who owns that service? By the time you've mapped it all out, a new CVE drops for a Python library you've never heard of, and the cycle starts again.
This was our reality. Dozens of repositories. Four primary languages: Java, Python, Node, and Go. Each child service manages its own dependency versions. Each team patching at their own pace. Each vulnerability scan produces a different slice of a fragmented picture.
We needed a single source of truth — and we needed security gates layered through every stage of the supply chain, from the moment a developer opens a pull request to the moment an image sits at rest in our registry. Here's how we built it.
The Architecture at a Glance
Our supply chain security model is structured into three interconnected layers, each owning a specific phase of the lifecycle:

GitLab — The Source Code Management and Governance layer where dependencies are declared and continuously audited.
Jenkins Pipeline — The Continuous Integration engine where code becomes an artifact and security gates decide whether it lives or dies.
AWS Cloud — The storage and continuous monitoring layer where production-ready images are kept under perpetual watch.
Layer I: GitLab — Governance at the Source
This is where the development cycle originates, and it's where we made our most consequential architectural decision: centralize dependency declarations in a parent manifest, and let every service inherit from it.
The Parent File: A Global Manifest as the Source of Truth
The Parent File is our Base Packages File — a centralized Global Manifest for all package dependencies across our tech stacks: Python (PyPI), Go (Modules), and Java (Maven). It pins approved, version-controlled versions of every library used across the organization. No service gets to pick its own version of jackson-databind or requests or golang.org/x/crypto. The parent decides; the children inherit.
Java parent file structure:

Python parent structure:

Go parent module: uses replace directives and shared module versions to keep the dependency graph consistent across services.
The Service Repo: Application Logic, Inherited Versions
Each Service Repo is an Application Source Repository containing the functional microservice code and its specific business logic. It integrates with the Parent File to ensure every build adheres to Organizational Level Package Control. Child services declare what they need; the parent decides which version they get.
A child Java POM looks like this:

The result: no shadow dependencies, no version drift, no surprises. If a service is using a library, it's using the version security and platform engineering have approved.
The ISDP Cron Job: Continuous Audit Beyond the Build
Build-time scanning catches what's broken today. It misses what becomes broken tomorrow — when a zero-day drops at 2 AM against a library that was perfectly fine when your last build ran a week ago.
That's where our Cron Job comes in. It's a Scheduled Security Audit running in Jenkins, using Trivy as the scanner against our Parent Files every morning. Because the Parent Files are the dependency inventory for the entire platform, scanning them is equivalent to scanning every service simultaneously — without ever cloning a service repo.
By the time the team logs in, Trivy has produced a ranked list of what needs attention — across the entire platform.
Layer II: Jenkins Pipeline — Where Security Gates Get Real
The governance layer keeps our dependency graph honest. The Jenkins Pipeline is where that honesty gets enforced at build time, with multiple security gates standing between source code and a deployable image.
Merge Request: The Pipeline Trigger
Everything starts with a Merge Request. When a developer opens an MR against a service repository, it triggers the Jenkins Pipeline, which clones the service repo, resolves dependencies through the Parent File, and initiates the CI/CD workflow. No code becomes an artefact without passing through this gauntlet.
Docker Build Stage: Containerisation on a Hardened Foundation
In the Containerisation Phase, the pipeline compiles the source code from the cloned service repo into an OCI-compliant Docker Image. Critically, the build doesn't start from an arbitrary base image pulled off the public internet — it starts from a Baseline Image sourced from our internal ECR. That baseline is hardened, scanned, and trusted. Building on top of it means the final image inherits all of that hardening rather than starting from zero.
The output is a final built Docker image, ready to be deployed to Kubernetes — but not yet trusted. The next gate decides whether it earns that trust.
Image Scanning Stage: CrowdStrike Falcon CI/CD Scanner
The freshly built Docker image — which combines the Baseline Image and the cloned application code and — is now scanned for vulnerabilities and CVEs using the CrowdStrike Falcon CI/CD Scanner. This scan looks at every layer: the OS packages from the baseline, the language runtime, the application dependencies resolved from the parent manifest, and any artifacts produced during the build including the transient packages.
Pipeline Breaker Logic: The Automated Enforcement Controller
The Pipeline Breaker Logic is our Automated Enforcement Controller. It evaluates the telemetry from the security scans against a defined threshold — typically zero criticals and a bounded count of highs, depending on the service's risk tier. If the threshold is breached, the logic returns a failure status, terminating the pipeline.
By default, a vulnerable image cannot progress from this stage to ECR. The build fails, the developer gets a detailed report, and the parent manifest gets the version bump it needs — which then propagates to every other service on its next build. (We do support an audited Break Glass procedure for genuine emergencies, which we'll cover later.)
Layer III: AWS Cloud — Continuous Monitoring at Rest
Passing the build-time gates is necessary but not sufficient. An image that was clean on Tuesday can be vulnerable on Wednesday because the world's CVE feed never sleeps. Layer III is where we keep the guard tower.
ECR: The Secure Container Registry
Amazon Elastic Container Registry (ECR) serves as the secure, private repository for all container images that have successfully passed the CI security gates. If an image is in ECR, it has cleared every check up to this point. ECR is also the source of truth for the Baseline Images that future builds will start from — meaning the registry is both the destination for new builds and the foundation for the next ones.
Falcon ECR Scan: Deep, Continuous Vulnerability Monitoring
Using CrowdStrike Falcon as a secondary security provider, this stage performs deep scanning of images at rest in ECR — and it runs every 2 to 4 hours across every image in the registry.
That cadence matters. If a new CVE is published affecting a library baked into one of our images, we don't have to wait for the next build of that service to find out. Falcon will catch it on its next sweep, often within hours of the CVE being disclosed publicly. The findings flow into the same triage workflow as our morning Trivy scans, so the security team has a unified view regardless of where the vulnerability was detected.
Why This Layered Model Works

The architecture's strength is that no single tool, no single scan, and no single team owns vulnerability management end-to-end. Each layer does what it's best at:
Governance is enforced where dependencies are declared. The Parent File makes "shadow dependencies" structurally impossible. You can't use a version you didn't inherit.
Build gates catch what governance can't. Even with a clean parent file, the way a Docker image is assembled — base layers, OS packages, embedded binaries — can introduce vulnerabilities the parent file never sees. Falcon's CI/CD scan catches these, and the Pipeline Breaker turns the finding into an enforced decision rather than a recommendation.
Continuous monitoring catches what time introduces. A build that passed yesterday is not necessarily safe today. Falcon's ECR sweep ensures that the half-life of an undetected vulnerability in our registry is measured in hours, not days.
Two scanners are better than one. Trivy and Falcon have different vulnerability databases, different detection heuristics, and different blind spots. Running both means a CVE has to evade two independent systems to slip through. That's defense in depth applied to scanning itself.
The Break Glass Procedure: When the Gate Has to Open
Every mature security pipeline eventually meets the same scenario: a Critical CVE drops on a library with no upstream patch, and production is on fire with a hotfix that has to ship in thirty minutes. Without a defined procedure, security accidentally becomes a denial-of-service attack against the business. And not every CVE with a 10 CVSS score has an active exploit and reachable.
When a Pipeline Breaker can't be resolved by patching — because no fix exists, or because waiting costs more than shipping — the engineer raises a Break Glass exception. But the gate doesn't swing open on request. Before anything lands on the exclusion list, security runs a reachability and exploitability analysis to answer two questions that matter more than the CVSS score:
- Is the vulnerable code path actually reached by our application?
- Is the vulnerability exploitable in our deployment context?
The outcome lands in one of three buckets:
- Not reachable, not exploitable → Exclusion granted with justification and expiry. Pipeline unblocks.
- Reachable but not exploitable → Conditional exclusion with compensating controls.
- Reachable and exploitable → No exclusion. The team works the actual fix.

Approved exclusions are committed as structured ignore entries to a dedicated exceptions repository — not the parent manifest, not the service repo. Each one carries an owner, CVE reference, the analysis findings, an expiry date, and the linked ticket. At expiry, entries re-run the analysis rather than silently rolling over.
The result: legitimate emergencies get a path forward in minutes because the analysis is bounded and pre-defined, not improvised under pressure. "Let's just ship it" requests hit a process that surfaces the real cost of skipping the gate. Security stays a partner to the business, and the exclusion list stays a record of considered decisions rather than a graveyard of forgotten shortcuts.
SBOM Generation: Turning the Pipeline Into a Transparency Engine
With Parent Manifests and Trivy already in place, generating a Software Bill of Materials (SBOM) for every artifact is a small additional step that pays compounding dividends. We generate SBOMs directly from the Falcon Dashboard with a clear and deep visibility into each and every build, image, package and CVE.
The Falcon dashboard is our single source of truth for supply chain CVEs. It surfaces what's currently open, what's been remediated, what's been formally accepted as unfixable, and recent activity across the pipeline — with timestamps for when each vulnerability was first identified, when it was fixed, or how long it's been sitting unresolved.
Developer Experience: The new "Dependency Request" Workflow
Centralised dependency governance only works if it is faster than bypassing it. Developers file a structured Jira ticket with package, version, and purpose. A Security Engineer reviews licence compatibility, CVEs via Trivy, Snyk, or Grype, maintenance and provenance, and attack surface including permissions, sensitive data access, and malware scanning. Material-risk packages require Security EM sign-off; low-risk ones do not.
Approved dependencies are logged in a central registry with version pinned and review date recorded. The pipeline runs a fresh scan on the next build to validate the dependency in the real container context. Once rejections come with documented reasons and a safer alternative, the registry stops being seen as a constraint and becomes a service the security team provides.
A few hard-won lessons stand out from running this architecture
Transitive dependencies still bite. Pinning direct dependencies in the Parent File isn't enough if a patched direct dependency pulls in a vulnerable transitive. We extended the parent manifests to explicitly pin known-vulnerable transitives, and Trivy's output drives those decisions.
Parent version bumps need a change process. Changing a version in the Parent File affects every child service that inherits it. We treat parent manifest changes like API changes — pull requests require review, CI runs across a representative sample of child services, and breaking changes are communicated. False positives are a fact of life. Not every CVE Trivy or Falcon reports is exploitable in our context. We maintain exclusion lists with mandatory justifications.
Measuring the Impact
A few months into running this architecture, the numbers tell the story. Mean time to patch a critical CVE dropped from days to hours, because there's one Parent File to change instead of dozens of service repos. Version drift across services went to effectively zero, because there is no such thing as an "outdated" version relative to the parent — the parent is the version. The window between a CVE being published and our registry knowing about it shrank from days to hours, thanks to Falcon's continuous ECR scanning. And audit reporting became a non-event: pointing an auditor at the Parent Files, the Jenkins pipeline definition, and the Falcon dashboards covers most of the attack surface in minutes.
Closing Thoughts
Supply chain security fails when it is treated as a single checkpoint instead of a continuous property of the system. The architecture described here removes that failure mode by applying three principles end-to-end: least authority over versions, so no service chooses its own; defence in depth across the lifecycle, with Trivy on the manifests, Falcon at build and at rest, and a Pipeline Breaker turning findings into enforced decisions; and continuous verification, so an image that passed yesterday is re-evaluated today.
What this buys is not the absence of vulnerabilities, but a bounded window between disclosure and detection, a single place to act when that window opens, and an auditable record of every exception. And when governance is fast and reasoned, shadow dependencies disappear — not because they are forbidden, but because the supported path is genuinely easier.
Bug Bounty
Got what it takes to hack? Feel free to report a vulnerability in our assets and get yourself a reward through our bug bounty program. Find more details about policy and guidelines at https://www.halodoc.com/security
Join Us
Scalability, reliability and maintainability are the three pillars that govern what we build at Halodoc Tech. We are actively looking for engineers at all levels and if solving hard problems with challenging requirements is your forte, please reach out to us with your resumé at careers.india@halodoc.com
About Halodoc
Halodoc is the number one all-around healthcare application in Indonesia. Our mission is to simplify and deliver quality healthcare across Indonesia, from Sabang to Merauke. Since 2016, Halodoc has been improving health literacy in Indonesia by providing user-friendly healthcare communication, education, and information (KIE). In parallel, our ecosystem has expanded to offer a range of services that facilitate convenient access to healthcare, starting with Homecare by Halodoc as a preventive care feature that allows users to conduct health tests privately and securely from the comfort of their homes; My Insurance, which allows users to access the benefits of cashless outpatient services in a more seamless way; Chat with Doctor, which allows users to consult with over 20,000 licensed physicians via chat, video or voice call; and Health Store features that allow users to purchase medicines, supplements and various health products from our network of over 4,900 trusted partner pharmacies. To deliver holistic health solutions in a fully digital way, Halodoc offers Digital Clinic services including Haloskin, a trusted dermatology care platform guided by experienced dermatologists.We are proud to be trusted by global and regional investors, including the Bill & Melinda Gates Foundation, Singtel, UOB Ventures, Allianz, GoJek, Astra, Temasek, and many more. With over USD 100 million raised to date, including our recent Series D, our team is committed to building the best personalized healthcare solutions — and we remain steadfast in our journey to simplify healthcare for all Indonesians.