Tuesday, October 27, 2026

Shift-Left Security in Jenkins: Secrets, SAST, Dependency and Image Scanning

Security reviews at the end of a release find problems when they are most expensive to fix. “Shift left” simply means running the same checks inside every pipeline run, so a leaked key or a vulnerable dependency fails the build the day it is introduced. Here is the set of stages I add to Java pipelines.

The stages

pipeline {
  agent { label 'linux' }
  environment {
    IMAGE = "nexus.example.com:8443/apps/account-search:${BUILD_NUMBER}"
  }
  stages {
    stage('Secrets scan') {
      steps { sh 'gitleaks detect --source . --redact --exit-code 1' }
    }
    stage('Build & unit tests') {
      steps { sh 'mvn -B clean verify' }
    }
    stage('SAST + quality gate') {
      steps {
        withSonarQubeEnv('sonar') { sh 'mvn -B sonar:sonar' }
        timeout(time: 10, unit: 'MINUTES') {
          waitForQualityGate abortPipeline: true
        }
      }
    }
    stage('Dependency check') {
      steps {
        sh 'mvn -B org.owasp:dependency-check-maven:check -DfailBuildOnCVSS=7'
      }
    }
    stage('Image build + scan') {
      steps {
        sh 'docker build -t $IMAGE .'
        sh 'trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed $IMAGE'
        sh 'trivy image --format cyclonedx --output sbom.json $IMAGE'
        archiveArtifacts artifacts: 'sbom.json'
      }
    }
  }
}

What each stage catches

  • Secrets scan (gitleaks) – API keys, passwords and tokens committed to the repo. Run it first; it takes seconds.
  • SAST with a quality gate (SonarQube) – injection risks, weak crypto and bugs in your code. waitForQualityGate makes the gate a hard stop instead of a dashboard nobody reads.
  • Dependency check (OWASP) – known CVEs in third-party libraries. Start with a CVSS threshold of 7 and tighten later.
  • Image scan (Trivy) – vulnerable OS packages and runtimes in the final image. --ignore-unfixed keeps the build focused on things you can actually fix.
  • SBOM – a CycloneDX list of everything inside the image. When the next big library vulnerability is announced, you can answer “are we affected?” in minutes.

Making it stick

  1. Put these stages in the shared library, not in each Jenkinsfile, so every team gets them and updates roll out in one place.
  2. Pin scanner and tool versions and pull them through your Nexus proxy. Recent npm and PyPI compromises showed that floating tags like latest are a supply-chain risk in themselves.
  3. Have a documented exception process (suppression files with an owner and an expiry date). Otherwise people disable the stage the first time it blocks a release.
  4. Start in warning mode for a week, fix the backlog, then switch to failing builds.

The goal is not zero findings; it is that every new risk is visible, owned and decided on before it reaches production.

Blue-Green vs Canary Deployments on Kubernetes

A plain Kubernetes rolling update is fine for most changes, but it has one weakness: once the rollout finishes, every user is on the new version, and rolling back means another rollout. For risky releases, two strategies give you more control: blue-green and canary.

Blue-green: switch all traffic at once

Run the new version (green) next to the current one (blue), test it, then move the Service selector.

# two Deployments with different version labels
#   account-search-blue   labels: app=account-search, version=blue
#   account-search-green  labels: app=account-search, version=green

apiVersion: v1
kind: Service
metadata:
  name: account-search
spec:
  selector:
    app: account-search
    version: blue      # change to green to cut over
  ports:
  - port: 80
    targetPort: 8080
# cut over
kubectl patch service account-search \
  -p '{"spec":{"selector":{"app":"account-search","version":"green"}}}'

# instant rollback
kubectl patch service account-search \
  -p '{"spec":{"selector":{"app":"account-search","version":"blue"}}}'

Good for: releases you can fully test before exposure, and instant rollback. Cost: double capacity during the switch, and database changes must work for both versions.

Canary: shift traffic gradually

Send a small share of real traffic to the new version, watch metrics, then increase. Argo Rollouts makes this declarative:

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: account-search
spec:
  replicas: 10
  selector:
    matchLabels: { app: account-search }
  template:
    # same pod template as your Deployment
  strategy:
    canary:
      steps:
      - setWeight: 10
      - pause: { duration: 10m }
      - setWeight: 50
      - pause: { duration: 10m }
      - setWeight: 100

Without a service mesh or ingress integration, the weight is approximated by replica counts. With NGINX Ingress, Istio or a Gateway API controller, Rollouts can split traffic precisely. Add an AnalysisTemplate that queries Prometheus (error rate, p95 latency) and the rollout aborts and rolls back by itself when the canary looks worse than stable.

Good for: changes whose real-world behaviour you can't fully test. Cost: you need good metrics, and two versions serve users at the same time.

Which one should you use?

  • Rolling update – the default for routine changes.
  • Blue-green – big-bang releases, strict cut-over windows, or when you need instant rollback.
  • Canary – high-traffic services where a small blast radius and metric-driven promotion matter.

Whatever you pick, make database migrations backward compatible (expand, then contract). That one discipline makes every strategy here safe to roll back.

Kubernetes Requests and Limits for Java Apps: Avoiding OOMKilled and CPU Throttling

“The pod keeps getting OOMKilled but the heap is only 512 MB.” “Latency doubles every few seconds and the CPU graph looks fine.” Both of these come from the same root cause: Kubernetes resource settings that don't match how the JVM actually uses memory and CPU.

Memory: the heap is not the whole process

A Java process uses heap plus metaspace, thread stacks, the code cache, GC structures and direct buffers. If you set -Xmx512m and a 512Mi container limit, the kernel will kill the container long before Java ever throws an OutOfMemoryError.

Let the JVM size itself from the container limit instead:

env:
- name: JAVA_TOOL_OPTIONS
  value: "-XX:MaxRAMPercentage=75 -XX:+ExitOnOutOfMemoryError"
resources:
  requests:
    memory: "1Gi"
    cpu: "500m"
  limits:
    memory: "1Gi"
  • 75% leaves room for non-heap memory. For apps with many threads or heavy off-heap use (Netty, large caches) go to 60–65%.
  • Memory request = memory limit. Memory is not compressible; a pod that bursts above its request is the first to be evicted when the node is under pressure.
  • ExitOnOutOfMemoryError makes a heap OOM a clean restart instead of a half-dead JVM.

CPU: limits cause throttling

CPU limits are enforced in 100 ms windows. A JVM with several GC and JIT threads can use its whole quota in the first 20 ms and then sit throttled for 80 ms. That shows up as random latency spikes even though average CPU looks low.

  • Set a realistic CPU request so the scheduler places pods sensibly.
  • Either don't set a CPU limit, or set it well above the request (2–4×) for latency-sensitive services.
  • Watch container_cpu_cfs_throttled_periods_total in Prometheus; if it climbs, the limit is too tight.
  • The JVM sizes thread pools from the CPU count it sees. If needed, pin it with -XX:ActiveProcessorCount=2.

Finding the right numbers

  1. Run under realistic load and watch kubectl top pod plus JVM metrics from Actuator/Micrometer.
  2. Install the Vertical Pod Autoscaler in recommendation-only mode and read its suggestions for a week.
  3. Set requests around normal peak usage, then revisit after big releases.

Quick diagnosis

  • Reason: OOMKilled, exit code 137 – container limit hit; lower MaxRAMPercentage or raise the limit.
  • java.lang.OutOfMemoryError in logs – heap is too small for the workload, or there is a leak.
  • Latency spikes, CPU looks low – check throttling before you blame the code.

Injecting HashiCorp Vault Secrets into Kubernetes Pods

Kubernetes Secrets are only base64-encoded, they end up in Git or Helm values far too easily, and rotating a database password means redeploying everything. With HashiCorp Vault and the Vault Agent Injector, pods authenticate with their Kubernetes service account and receive secrets as files, without the application knowing Vault exists.

1. Enable Kubernetes auth in Vault

vault auth enable kubernetes

vault write auth/kubernetes/config \
    kubernetes_host="https://kubernetes.default.svc:443"

When Vault runs inside the same cluster, it can use its own service account token and CA to validate other pods' tokens.

2. Policy and role

# account-search.hcl
path "secret/data/account-search/*" {
  capabilities = ["read"]
}
vault policy write account-search account-search.hcl

vault write auth/kubernetes/role/account-search \
    bound_service_account_names=account-search \
    bound_service_account_namespaces=apps \
    policies=account-search \
    ttl=1h

Only pods running as the account-search service account in the apps namespace can log in with this role, and they can only read their own path.

3. Annotate the Deployment

spec:
  template:
    metadata:
      annotations:
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "account-search"
        vault.hashicorp.com/agent-inject-secret-db.properties: "secret/data/account-search/db"
        vault.hashicorp.com/agent-inject-template-db.properties: |
          {{- with secret "secret/data/account-search/db" -}}
          spring.datasource.username={{ .Data.data.username }}
          spring.datasource.password={{ .Data.data.password }}
          {{- end }}
    spec:
      serviceAccountName: account-search

The injector adds an init container and a sidecar. The rendered file appears at /vault/secrets/db.properties and the sidecar keeps it fresh when the secret changes or the lease renews.

4. Let Spring Boot read it

# application.yml
spring:
  config:
    import: "optional:file:/vault/secrets/db.properties"

No Vault SDK, no code change. Locally, developers use their own properties file at the same path.

Things to watch

  • The template path uses secret/data/... for KV version 2. Forgetting /data/ is the most common “permission denied”.
  • Rotation is not reload. The file updates, but a running connection pool keeps the old password until restart unless your app re-reads it. Pair rotation with a rolling restart or use Vault dynamic database credentials.
  • Alternatives: the Vault Secrets Operator syncs into native Kubernetes Secrets, and the Secrets Store CSI driver mounts secrets as volumes. Pick one approach per cluster and stick to it.
  • Keep Vault patched and restrict who can restore snapshots; Vault is now the most valuable target in the cluster.

Terraform Remote State on S3 with Locking: A Practical Setup

The fastest way to break Terraform for a whole team is to keep terraform.tfstate on someone's laptop. Two people apply at the same time, one state file wins, and the other half of your infrastructure is now “unknown” to Terraform. A remote backend with locking fixes this.

1. The state bucket

Create the bucket once (by hand or in a tiny bootstrap Terraform project with local state):

  • Versioning on – every state write is recoverable.
  • Block all public access and default encryption (SSE-S3 or SSE-KMS).
  • A bucket policy that only allows your CI role and a small admin group.

2. The backend block

terraform {
  required_version = ">= 1.10"

  backend "s3" {
    bucket       = "shailapps-tfstate"
    key          = "envs/dev/network/terraform.tfstate"
    region       = "us-east-1"
    encrypt      = true
    use_lockfile = true
  }
}

use_lockfile = true uses S3's native conditional writes to create a .tflock object next to the state, available from Terraform 1.10. On older versions you need a DynamoDB table with a LockID string key and dynamodb_table = "..." in the backend instead; that option is now deprecated, so plan the upgrade.

3. Move existing state

terraform init -migrate-state

Terraform copies the local state into S3. Check the object exists, then delete the local file so nobody applies from it by accident.

4. Split state by blast radius

envs/
  dev/
    network/   -> key = envs/dev/network/terraform.tfstate
    eks/       -> key = envs/dev/eks/terraform.tfstate
    apps/
  prod/
    network/
    eks/

One giant state means every plan touches everything and a mistake can destroy unrelated resources. Separate states per environment and per layer keep plans fast and failures small. Read outputs across layers with the terraform_remote_state data source or, better, by looking resources up with data sources.

5. Running it from CI

  • Authenticate with an IAM role (OIDC from your CI system), never long-lived keys in variables.
  • Run terraform plan -out=tfplan on the pull request and terraform apply tfplan only after approval, so what you reviewed is exactly what gets applied.
  • If a run dies and leaves a lock behind, use terraform force-unlock LOCK_ID only after you are sure no other apply is running.

With this in place, concurrent applies fail fast with a clear lock error instead of silently corrupting state.

Nexus as a Single Proxy for Maven, npm and Docker in CI

Our CI builds used to fail a few times a month for reasons that had nothing to do with our code: Maven Central was slow, npm returned a 503, or Docker Hub rate-limited the build agents. Putting Sonatype Nexus Repository in front of every public registry made those failures disappear, and it gave us one place to control what enters the build.

The repository model

For each format, create three kinds of repository:

  • proxy – caches a remote registry (Maven Central, npmjs, Docker Hub).
  • hosted – stores what your team publishes (release JARs, internal npm packages, app images).
  • group – one URL that combines hosted + proxies. Clients only ever talk to the group.
maven-public  (group)  = maven-releases + maven-snapshots + maven-central-proxy
npm-group     (group)  = npm-hosted + npmjs-proxy
docker-group  (group)  = docker-hosted + dockerhub-proxy

Maven: one mirror for everything

<!-- ~/.m2/settings.xml on the build agents -->
<settings>
  <mirrors>
    <mirror>
      <id>nexus</id>
      <mirrorOf>*</mirrorOf>
      <url>https://nexus.example.com/repository/maven-public/</url>
    </mirror>
  </mirrors>
</settings>

In Jenkins, manage this file with the Config File Provider plugin and inject it with configFileProvider, so credentials for deploying to maven-releases never live in a repo.

npm

npm config set registry https://nexus.example.com/repository/npm-group/

Or commit an .npmrc with that line to each project, so local builds and CI resolve packages the same way.

Docker

Give the Docker group repository its own HTTPS connector or a path-based route through your reverse proxy, then pull through it:

docker pull nexus.example.com:8443/library/eclipse-temurin:21-jre
# in Dockerfiles
FROM nexus.example.com:8443/library/maven:3.9-eclipse-temurin-21 AS build

Push application images to the hosted repository and let Kubernetes pull them from the same group URL.

Housekeeping that saves you later

  1. Cleanup policies. Delete snapshot and feature-branch images older than 30 days, then run the compact blob store task. Otherwise the disk fills quietly.
  2. Separate blob stores per format, so a runaway Docker cache cannot starve Maven.
  3. Disable anonymous access unless you really need it, and use service accounts per pipeline.
  4. Keep Nexus itself patched. It sits in the middle of your supply chain; a compromise here reaches every build.
  5. Block known-bad versions. When a malicious package version is announced, removing or blocking it in the proxy protects every build agent at once.

The result: faster builds, no rate limits, and a single choke point where you can stop a bad dependency before it reaches production.

GitOps with Argo CD: The App-of-Apps Pattern

Once you have more than a handful of applications in Argo CD, clicking “New App” in the UI stops scaling. Nobody remembers which settings were used, and rebuilding a cluster means recreating every Application by hand. The app-of-apps pattern puts the Argo CD Applications themselves in Git, managed by one root Application.

Repository layout

gitops/
├── root-app.yaml            # applied once, by hand
├── apps/                    # one Application per service
│   ├── account-search.yaml
│   ├── payments-api.yaml
│   └── ingress-nginx.yaml
└── charts/ or manifests/    # the actual workloads

The root Application

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://git.example.com/platform/gitops.git
    targetRevision: main
    path: apps
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Apply it once with kubectl apply -n argocd -f root-app.yaml. From then on, adding a service is a pull request that drops a new file into apps/.

A child Application

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: account-search
  namespace: argocd
  annotations:
    argocd.argoproj.io/sync-wave: "10"
spec:
  project: apps
  source:
    repoURL: https://git.example.com/platform/gitops.git
    targetRevision: main
    path: charts/account-search
    helm:
      valueFiles: [values-prod.yaml]
  destination:
    server: https://kubernetes.default.svc
    namespace: account-search
  syncPolicy:
    automated: { prune: true, selfHeal: true }
    syncOptions: [CreateNamespace=true]

Tips from running it

  • Use sync waves for ordering. Give platform pieces (ingress controller, cert-manager, operators) a lower sync-wave than business apps so CRDs exist before anything uses them.
  • Use AppProjects as guard rails. Restrict which repos and namespaces the apps project can deploy to, so a typo cannot deploy into kube-system.
  • Be careful with prune on the root. Deleting a file in apps/ deletes the Application and, with the default finalizer, its workloads. That is usually what you want, but review those PRs carefully.
  • Consider ApplicationSets once the child files all look the same. A Git directory generator can create one Application per folder automatically.
  • Keep Argo CD patched. The repo-server holds your Git and Helm credentials; treat its upgrades like any other internet-facing component.

The payoff: a new cluster becomes “install Argo CD, apply one file, wait”, and the state of every environment is reviewable in Git.

Smaller, Safer Docker Images for Spring Boot with Multi-Stage Builds

The first Docker image I built for a Spring Boot service was 780 MB: a full JDK, Maven, the source code and a fat JAR, all running as root. It worked, but every deploy pushed hundreds of megabytes and every scan found dozens of CVEs in tools the app never used. A multi-stage build fixes all of that.

The Dockerfile

# ---- 1. build ----
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /src
COPY pom.xml .
RUN mvn -B -q dependency:go-offline
COPY src ./src
RUN mvn -B -q package -DskipTests

# ---- 2. split the jar into layers ----
FROM eclipse-temurin:21-jre AS extract
WORKDIR /app
COPY --from=build /src/target/*.jar app.jar
RUN java -Djarmode=tools -jar app.jar extract --layers --launcher --destination extracted

# ---- 3. runtime ----
FROM eclipse-temurin:21-jre
RUN useradd --system --uid 10001 appuser
WORKDIR /app
COPY --from=extract /app/extracted/dependencies/ ./
COPY --from=extract /app/extracted/spring-boot-loader/ ./
COPY --from=extract /app/extracted/snapshot-dependencies/ ./
COPY --from=extract /app/extracted/application/ ./
USER 10001
ENV JAVA_TOOL_OPTIONS="-XX:MaxRAMPercentage=75 -XX:+ExitOnOutOfMemoryError"
EXPOSE 8080
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]

Why each piece matters

  • Build stage is thrown away. Maven, the JDK and your source never reach the final image.
  • dependency:go-offline before COPY src. Dependencies get their own cached layer, so a code-only change does not re-download half of Maven Central.
  • Layered JAR. Spring Boot splits the app into dependencies, loader and application layers. Only the small application layer changes between releases, so pushes and pulls are fast. (The -Djarmode=tools syntax is Spring Boot 3.3+; older versions use -Djarmode=layertools -jar app.jar extract.)
  • JRE, not JDK. Smaller image and fewer packages for the scanner to flag. Distroless or Alpine-based JREs go smaller still if your team is comfortable debugging without a shell.
  • Non-root user. Many Kubernetes clusters enforce runAsNonRoot; set it in the image so it just works.
  • MaxRAMPercentage sizes the heap from the container memory limit instead of a hard-coded -Xmx.

Don't forget .dockerignore

target/
.git/
.idea/
*.iml
**/*.log

Without it, COPY sends your whole working tree (including .git) to the build context.

Result

For a typical service the image dropped from about 780 MB to under 250 MB, rebuilds after a code change take seconds, and the image scan in CI went from dozens of findings to a handful that actually belong to the runtime. Run trivy image on both versions and compare for yourself.

Kubernetes Startup, Liveness and Readiness Probes for Spring Boot

Most Spring Boot services I see on Kubernetes have exactly one probe: a liveness check on /actuator/health. It works until the database has a hiccup, the health endpoint goes DOWN, and Kubernetes restarts every pod at the same moment. The fix is to use the three probe types for what they are actually for.

What each probe means

  • startupProbe – “Has the app finished booting?” Liveness and readiness are not checked until this passes. Perfect for slow JVM starts.
  • livenessProbe – “Is the process stuck beyond repair?” If it fails, the kubelet restarts the container.
  • readinessProbe – “Should this pod receive traffic right now?” If it fails, the pod is removed from the Service endpoints but not restarted.

Turn on the Spring Boot probe groups

# application.yml
management:
  endpoint:
    health:
      probes:
        enabled: true
  health:
    livenessstate:
      enabled: true
    readinessstate:
      enabled: true

This exposes /actuator/health/liveness and /actuator/health/readiness. Liveness only reflects the application's internal state, so a slow database will not restart your pods. If you want a dependency to affect traffic, add it to the readiness group only:

management:
  endpoint:
    health:
      group:
        readiness:
          include: readinessState, db

The Deployment

containers:
- name: account-search
  image: nexus.example.com/apps/account-search:1.8.2
  ports:
  - containerPort: 8080
  startupProbe:
    httpGet: { path: /actuator/health/liveness, port: 8080 }
    periodSeconds: 5
    failureThreshold: 30        # up to 150s to boot
  livenessProbe:
    httpGet: { path: /actuator/health/liveness, port: 8080 }
    periodSeconds: 10
    failureThreshold: 3
  readinessProbe:
    httpGet: { path: /actuator/health/readiness, port: 8080 }
    periodSeconds: 5
    failureThreshold: 3

Rules I follow

  1. Never check external dependencies in liveness. Restarting your pod will not fix the database.
  2. Use startupProbe instead of a huge initialDelaySeconds. It protects slow starts without delaying failure detection later.
  3. Expose actuator on a separate management port (management.server.port: 8081) if the app port is public, and point probes there.
  4. Enable graceful shutdown (server.shutdown: graceful) so readiness flips to refusing traffic before the pod stops.

With this split, a dependency outage drains traffic instead of triggering a restart storm, and a genuinely hung JVM still gets recycled.

Building a Jenkins Shared Library for Multi-Project CI/CD

Every team I have worked with starts with a Jenkinsfile copied from the last project. Six months later there are forty slightly different pipelines, and a single change to the security scan step means forty pull requests. A Jenkins shared library fixes this: the pipeline logic lives in one Git repo, and each application keeps a five-line Jenkinsfile.

Repository layout

jenkins-shared-lib/
├── vars/
│   ├── javaPipeline.groovy      // full pipeline for Maven/Spring Boot apps
│   ├── nodePipeline.groovy
│   └── dockerBuild.groovy       // reusable step
├── src/org/shailapps/ci/
│   └── Utils.groovy             // plain Groovy helpers
└── resources/
    └── templates/sonar-project.properties
  • vars/ – global steps. The file name becomes the step name (javaPipeline()).
  • src/ – normal Groovy classes, imported with import org.shailapps.ci.Utils.
  • resources/ – static files, loaded with libraryResource 'templates/sonar-project.properties'.

A pipeline as a step

// vars/javaPipeline.groovy
def call(Map cfg = [:]) {
  def jdk   = cfg.get('jdk', 'jdk21')
  def image = cfg.get('image', env.JOB_BASE_NAME)

  pipeline {
    agent { label 'linux' }
    tools { jdk jdk; maven 'maven3' }
    options { timestamps(); disableConcurrentBuilds() }
    stages {
      stage('Build & Test') {
        steps { sh 'mvn -B clean verify' }
        post  { always { junit '**/target/surefire-reports/*.xml' } }
      }
      stage('Sonar') {
        steps { withSonarQubeEnv('sonar') { sh 'mvn -B sonar:sonar' } }
      }
      stage('Image') {
        when { branch 'main' }
        steps { dockerBuild(name: image) }
      }
    }
  }
}

The application side

@Library('jenkins-shared-lib@v1.4.0') _
javaPipeline(jdk: 'jdk21', image: 'account-search')

That is the whole Jenkinsfile. Register the library once under Manage Jenkins → System → Global Pipeline Libraries with the name jenkins-shared-lib and your Git URL.

Lessons learned

  1. Pin versions. Reference a tag (@v1.4.0), not @main. A broken commit on main should not break every build in the company at once.
  2. Keep config in maps. Pass options as a Map with sensible defaults, so adding a new option never breaks existing callers.
  3. Test the library. Use the JenkinsPipelineUnit framework to unit-test your vars/ steps before tagging a release.
  4. One sample project per stack. Keep a tiny Java, Node and Python sample repo that runs the latest library version nightly. It is the cheapest early warning you will ever get.

Once the library is in place, adding a new scan or a new deploy target becomes one pull request instead of forty.

Critical Vulnerabilities — October 2026

SECURITY BRIEFING · DEVOPS STACK — Snapshot: 27 October 2026 — Late Sep – 27 Oct disclosures

Critical vulnerabilities to act on now

The big items from the last 30 days across your stack — GitLab, Jenkins, Argo CD, Kubernetes/OpenShift, Docker/containerd, Vault, n8n, Ollama, Node, Spring/Java, Nexus, Rundeck, Azure/AWS and the npm/PyPI supply chain. One cluster is not a vulnerability at all: malicious code was actually shipped. Start there.

Confidence key: items marked verified live were re-read on the vendor page on 27 Oct; index items rest on two independent secondary sources or a vendor-advisory summary. Score discrepancies and unverified items are flagged where they occur — nothing below is silently smoothed over.

Note: the original briefing's interactive search and filter controls do not carry over to Blogger; all readable content is included below.

PATCH IN THIS ORDER — from the research action list

  1. TODAY — Incident, not patch: hunt LiteLLM 1.107.0, tensorlake 0.5.144 and @subql/common 5.8.3 in caches, lockfiles, CI images and floating Docker tags. Rebuild clean and rotate every reachable credential.
  2. TODAY — Internet-facing: Argo CD → 3.3.15 / 3.4.10 / 3.5.4 · GitLab AI Gateway → 19.2.4 / 19.3.2 / 19.4.1 · NGINX → 1.30.6 / 1.31.7 · Ollama → ≥ 0.35.0 and off the public internet.
  3. THIS WEEK — Control plane: Jenkins 2.580 / LTS 2.568.3 + Script Security 1422 · Vault 2.1.2 train · n8n 2.38.2 / 2.37.7 / 1.123.76 · Nexus ≥ 3.97.0 · OpenShift console stream.
  4. THIS WEEK — Fleet runtimes: Docker Engine ≥ 29.9.2 + containerd ≥ 2.4.2 on every host and CI runner · rebuild JDK/Go base images for the eMudhra CA compromise · Node off v20 · Python off 3.10 · .NET off 8/9 before 10 Nov.
  5. THIS WEEK — Cloud + Kubernetes: verify the Azure 10.0s in MSRC / Azure health · K8s controller-manager → 1.34.12 / 1.35.9 / 1.36.5 / 1.37.1 · patch node kernels for PwnEscape and reboot rolling.
  6. NEXT — Then: Spring Framework 7.0.9 (or commercial support) · Rundeck ≥ 6.2.0 · KEV sweep (Collabora, Arista, Plex, PAN-OS…) · rotate any credential that lived on a repo-server, Jenkins controller, Vault host, n8n or Ollama host you can't prove was unexposed.

1 · Supply chain — confirmed malicious shipments

Treat as an incident: assume compromise, rebuild, rotate.

LiteLLM 1.107.0 — PyPI wheels + Docker/GHCR images, credential stealer & RAT loader

Status: Malicious release — no CVE. Shipped & observed. Verified live. Version 1.107.0 (7 wheels + sdist) and images v1.107.0 plus floating tags latest, main-latest, main-stable were malicious on 21 Oct 2026 — about 2 hours on PyPI (11:39–13:23 UTC), Docker tags to about 14:20/17:13 UTC. Last clean release: 1.106.2. The payload was pushed via stolen GitHub CLI OAuth tokens of 3 engineers, through a lookalike fork Berri-AI/litellm (real org: BerriAI/litellm). Observed on Windows/macOS endpoints; the second-stage remote-code loader was not confirmed executed — its domain pointer was empty at check time.

Affected: Any host/CI agent that installed or built with litellm 1.107.0 on/around 21 Oct. Fixed / clean: Pin to 1.106.2, or a later clean release only after verifying the digest. Persistence files: cook.pth, cook.py, sitecustomize.py in site-packages — outside the litellm dir, so deleting litellm alone is not enough.

Remediation — incident response steps:

  • Do NOT run python/pip inside a suspect environment to check — the payload hooks Python startup.
  • Isolate the host/agent; rebuild the image/environment from verified clean artifacts.
  • Confirm cook.pth, cook.py and sitecustomize.py are gone from site-packages.
  • Rotate, from a clean machine: model-provider keys, cloud/workload credentials, CI/CD + registry tokens, DATABASE_URL / proxy credentials.

Sources: Microsoft Security Blog · Datadog Security Labs · The Hacker News

tensorlake npm 0.5.144 — Shai-Hulud family credential-stealing worm

Status: Malicious release — no CVE. Shipped. Published 8 Oct 2026 (malicious commit 7 Oct via a maintainer account), plus 6 tensorlake-native-* 0.5.144 binaries. Preinstall hook node lib/setup.mjs runs a ~856 KB payload (lib/MathSymbol.js) — a Bun-based worm.

Affected: tensorlake 0.5.144 and the six tensorlake-native-* 0.5.144 binaries. Remediation: Remove 0.5.144 everywhere, pin to a clean version, audit lockfiles/caches/CI images. Rotate: npm/GitHub tokens, AWS/GCP/Azure, Vault, K8s, SSH, .env files, AI-tool configs; check for planted GitHub Actions workflows and packages republished under victim identities.

Sources: HuntAegis · Exine

@subql/common 5.8.3 — SubQuery npm compromise, reachable transitively

Status: Malicious release — no CVE. Shipped. Only 5.8.3 is malicious (clean predecessor 5.8.2), but it is reachable transitively via @subql/cli 6.6.3, @subql/node-core 19.3.1, @subql/query 2.25.0 and @subql/node-ethereum 6.5.0 — 19 of 77 packages reviewed had a path. Install hook runs dist/project/readers/manifest-cache.js; C2 at ci-artifacts.dev/router. npm latest was repointed to 5.8.2 by 23 Oct, but old lockfiles/caches still prove exposure. The issue was still open on 23 Oct and the attacker's access route was not disclosed.

Remediation: Block 5.8.3 in mirrors/caches, force 5.8.2 (or a later clean release), audit old lockfiles, build logs and caches, rotate credentials. Look for: branch dependabot/github_actions/format/setup-formatter and spoofed github-advanced-security[bot] commits.

Source: VigiHQ

MALFEX npm campaign — 8 packages, Windows RAT + stealer

Status: Malicious packages — no CVE. Long-running since Aug 2023. Packages: function-flag, function-color, cdn-img-fetch (still live in the 1 Oct snapshot), tlxbnhd, tldriver, mxdriver, img-to-native, native-runner — 40,767 downloads by 1 Oct (3,017 in the prior week). Payload: Overlord RAT (Solana-based C2) plus a stealer targeting Discord, browsers, Telegram and crypto wallets.

Remediation: Remove/block all 8, scan Windows dev/CI hosts, rotate browser/Discord/crypto credentials. Also in the window: the testmetest / Miasma-variant npm worm — 18 packages published 19–25 Oct (accounts alexjoipamiba, testnewtestme, iobandamwer); postinstall decrypts a worm via a native addon, steals credentials, spreads, and a wiper deletes the home directory if the GitHub token is revoked. No confirmed third-party victims as of 22 Oct (operator's own test accounts only, per SafeDep) — block the accounts/packages and rotate tokens if touched.

Sources: The Hacker News — MALFEX · SafeDep — testmetest

2 · Argo CD — four CVSS 9.9 flaws

Coordinated release, 7 Oct. The highest-CVSS cluster in the stack.

Argo CD repo-server cluster — fix: 3.3.15 / 3.4.10 / 3.5.4 / 3.6.0-rc2

Severity: CVSS 9.9 ×4, 8.8. Exploitation: No confirmed exploitation / no public PoC reported. All five flaws land in repo-server, which holds Git, Helm and OCI credentials — treat a repo-server compromise as a credential compromise and rotate its credentials if any precondition existed. Argo CD 2.x and 3.0–3.2 are out of support and get no patch — upgrade to a supported line.

CVE-2026-77459 · 9.9 — AppProject delete-hook bypass (≥ 2.10.0)

PreDelete/PostDelete hooks skipped AppProject destination/resource checks; a Git writer could commit a forbidden hook (e.g. a cluster-wide role binding) created with application-controller credentials when an Application is deleted. Workaround: restrict Git write + Application delete, audit existing Pre/PostDelete hooks, least-privilege controller RBAC.

CVE-2026-113708 · 9.9 — Kustomize remote-ref command injection (≥ 2.7.0)

A version value is passed to git fetch without option separation; repo content or the manifest-upload API (only Application get on an existing Application needed) can run commands in the repo-server. Blocking repo-server egress does not stop the local-transport path. Limited workaround: pin Kustomize apps to an explicitly installed Kustomize < 5.0 (e.g. 4.5.7) if compatible — the upgrade is the complete fix.

CVE-2026-113709 · 9.9 — Kustomize Helm config-home plugin execution (≥ 2.1.0)

With Kustomize Helm rendering enabled, a repo can steer Helm's config home to a repo folder and Helm loads/runs a plugin as the repo-server. Workaround: remove --enable-helm from kustomize.buildOptions (only if apps don't depend on it). Breaking change on upgrade: Kustomize Helm builds no longer honour helmGlobals.configHome.

CVE-2026-113672 · 9.9 — Jsonnet import file read (≥ 0.9.0)

The Jsonnet importer is not confined to the repo root: absolute paths, ../ and symlinks read any repo-server-readable file (/proc/self/environ, SA tokens, private keys, repo creds); exfiltration via ConfigMap (Secrets are redacted); a /dev/zero import can exhaust memory. Upload path needs only Application get on a directory Application.

CVE-2026-55797 · 8.8 — SSH proxy command injection (≥ 2.11.0)

Proxy host/port are embedded unsanitised in the SSH ProxyCommand; a user who can create/update a repo or credential template runs commands in the repo-server and reaches Git/Helm/OCI creds. Audit repo Secrets/credential templates for unexpected proxy values.

Also in the same release

Oversized OCI manifest DoS 6.5 (manifests > 4 MiB now rejected — breaking change), extension-proxy RBAC gap 4.9, and a login rate-limit bypass (CVE-2025-61560).

Sources: Suriq · SecurityOnline · Release v3.3.15 · Safeguard — 55797 · StackFlag — 113672

3 · GitLab

The AI Gateway is a separate component — upgrading GitLab core does not fix it.

CVE-2026-90970 — GitLab AI Gateway sandbox escape → arbitrary command execution on the gateway

Severity: CVSS 9.9. Exploitation: No exploitation per CISA assessment at disclosure · no public PoC. Prompt-template sandbox escape in the self-hosted AI Gateway only. Requires an authenticated user with Duo Agent Platform access who can create/modify a custom flow. GitLab.com, GitLab Dedicated and self-managed instances using a GitLab-hosted gateway are already patched — no action. No workaround stated. Gateway holds JWT signing keys for GitLab↔gateway traffic and model-provider connections — rotate those keys if exposure is plausible.

Affected: AI Gateway 18.1.6–19.2.3, 19.3.0–19.3.1, 19.4.0. Fixed (2 Oct 2026): 19.2.4 / 19.3.2 / 19.4.1 — update the gateway image directly. Also in the 12 Oct release: CVE-2026-16301 (8.5, Developer-role bypass of protected-branch pipeline controls) and CVE-2026-14814 (8.2, cached-file pattern manipulation → pipeline code execution / env leak) — same 19.2.8 / 19.3.5 / 19.4.2 patch train.

Sources: The Hacker News · SecurityAffairs — 12 Oct release

4 · NGINX — edge / ingress

Three heap flaws, 14 Oct. RCE only if ASLR is disabled/bypassed, per F5 — but worker crash/DoS is on the table.

NGINX memory-corruption trio — fix: Open Source 1.30.6 / 1.31.7 · Plus 37.0.5.1 / 37.1.0.1

Severity: CVSS 9.2 (V4.0) / 8.1 (V3.1). Exploitation: No confirmed exploitation · no public PoC as of 26 Oct.

CVE-2026-15176 — heap buffer overflow, alias + rewrite + $request_filename

NGINX Open Source 0.1.29–1.31.6 (present since 2005); Plus R33–R36, 37.0.0 before 37.0.5.1, 37.1.0 before 37.1.0.1. Workaround: avoid alias + $request_filename — use root.

CVE-2026-93479 — heap buffer overflow, map regex captures / script evaluation

Affected 1.30.0–1.30.5 and 1.31.2–1.31.6.

CVE-2026-97205 — use-after-free, aio + SSI include virtual

Affected 0.8.11–1.31.6. Requires both aio and SSI enabled — not a default-config flaw. Workaround: disable aio/SSI where possible.

Also fixed

CVE-2026-95101 (HTTP/3 request-body injection, medium) and CVE-2026-97629 (TLS session-ticket key left uninitialised when ssl_session_cache uses a shared zone with no explicit ssl_session_ticket_key — possible session forgery / client-cert bypass, medium; set an explicit ssl_session_ticket_key).

Sources: SecurityOnline · Windows Forum

5 · Containers, registries, runtimes

Check containerd on Kubernetes nodes separately — not just Docker Engine.

Docker Engine 29.9.1 / containerd 2.4.2 — then 29.9.2 for the CA fix

Severity: HIGH. Status: Verified live. CVE-2026-95839: pulling/importing via the containerd transfer service crashes containerd on a malformed digest in a child descriptor with io.containerd.manifest.subject. GHSA-8647-2jxj-p2rc: crafted Content-Length from an untrusted registry crashes containerd with parallel layer fetching (concurrent_layer_fetch_buffer). GHSA-hc94-p972-ch66: layer data not capped at declared size → disk exhaustion from a crafted image/malicious registry. Remediation: Upgrade Docker Engine to ≥ 29.9.1 — hosts pulling from third-party registries first. 29.9.2 (20 Oct) bumps Go runtime to 1.26.10 for CVE-2026-111943: chains with 8 compromised eMudhra intermediates are rejected. Registry chains relying on them will fail TLS — registries need a replacement chain. Same root cause as the Java item in §9.

Earlier containerd, via distro advisories (12 Oct): CVE-2026-47262 (crafted image → memory exhaustion/OOM-kill, DoS) and CVE-2026-53488 (CRI plugin propagates unvalidated image LABELs → arbitrary host command via a label-consuming plugin) — fixed 1.7.33 / 2.0.10 / 2.1.9 / 2.2.5 / 2.3.2. CVE-2026-53495 (CRI ExecSync drain goroutine block → memory exhaustion) — fixed 1.7.35 / 2.0.12 / 2.2.8 / 2.3.5. Check: containerd --version on every K8s node.

Sources: Docker Engine 29 release notes · Tenable — containerd advisories

Docker Hub “GhostPat” — CVE-2026-105445 — no installable patch; audit your PATs

Severity: CVSS 9.4 (V4.0). Exploitation: No evidence of exploitation found by Docker. Converting an account to an organisation passed the original owner's PATs to the new org owner (registry pull/push/delete within token scope; no web-UI, password, email or 2FA access). Flaw was open 26 Aug 2024 – 30 Sep 2026, fixed server-side 30 Sep; Docker analysed 304,000 pushes, found no evidence of exploitation, and deactivated transferred tokens on 8 Oct unless the owner opted to keep them. Remediation: Audit/rotate long-lived PATs, especially if you ever converted an account. Users not emailed by Docker are not affected.

Source: ReconBee

Podman — open item, not a clean bill

No critical Podman-specific CVE was found in this window in these searches. Do not assume absence — re-check the Podman release feed before reporting completeness. (You run Podman, not Docker Desktop, so this one is worth the manual check.)

6 · Jenkins — 2 September advisory

Still the current Jenkins patch train to verify. Controller compromise = full supply-chain compromise.

Jenkins core → 2.580 / LTS 2.568.3 + Script Security → 1422.v06869826dd9b_

Severity: HIGH ×SEVERAL. Exploitation: No exploitation reported in these sources. Verified live.

Core (fixed 2.580 / LTS 2.568.3)

CVE-2026-84645 — deserialization of PersistenceRoot types nested in user-submitted config.xml → Stapler handling → improperly protected Script Console → RCE. CVE-2026-84649 — CSRF crumb embedded in dynamically generated JS, loadable from a sibling subdomain (workaround if unpatched: disable Resource Root URL or put it on a different domain). CVE-2026-84652 — session fixation via the “remember me” cookie (same-site attacker). Also CVE-2026-84647/84648/84650 (High) and medium permission/deserialization issues 84646, 84651, 84653–84657.

Script Security Plugin — sandbox bypasses CVE-2026-92122–92129

7–8 flaws including a classpath-approval bypass and a TOCTOU: a Pipeline author can escape the Groovy sandbox → arbitrary code on the controller. Affected up to 1415.v9a_f9b_3a_c253d; fixed 1422.v06869826dd9b_. Separate medium issues 84658/84659 were fixed in 1415 — do not stop at 1415.

Same-window plugin updates

File Parameter (path traversal → RCE), Robot Framework 6.3.0 (path traversal → RCE, ≤ 6.2.2), GitLab, Bitbucket Push/Pull Request, Pipeline: Groovy Libraries, LDAP, SAML, Microsoft Entra ID, SonarQube Scanner 2.19.0, ThinBackup, Performance, Allure and more — full version table at the advisory. Parameterized Remote Trigger Plugin has NO fix — remove it.

Source: Jenkins Security Advisory 2026-09-02

7 · HashiCorp Vault + Terraform-adjacent

7 October batch — still unpatched in many estates.

Vault → Community 2.1.2 · Enterprise 2.1.2 / 1.21.12 / 1.20.17 / 1.19.23 (all 7 Oct 2026; also in 2.2.0)

Severity: HIGH (~8.0 top item).

HCSEC-2026-41 / CVE-2026-105816 — plugin catalog snapshot restore

Plugin catalog entries restored from a Raft snapshot were not verified to be inside plugin_directory → a privileged operator restoring a snapshot (Shamir-sealed cluster + external plugin dir) can execute arbitrary code on the Vault host.

HCSEC-2026-43 / CVE-2026-89322 — ACL deny bypass

Inconsistent ACL evaluation against canonical (case/padding) names → an authenticated delegated user bypasses an explicit deny, accesses a protected resource or assigns a denied policy → privilege escalation.

HCSEC-2026-40 / CVE-2026-105818 · HCSEC-2026-42 / CVE-2026-105820

ACME under the default sign-verbatim policy could put unverified SANs into certificates; and an Enterprise-only namespace policy-name path traversal.

After upgrading

Behaviour changes: out-of-directory plugin entries are refused at startup (re-register plugins), mixed-case policy rules must be rewritten, ACME SANs are rejected unless sign-verbatim-unsafe. A further root-alias fix needs 2.2.1 / Enterprise 1.21.13 / 1.19.24 (22 Oct, no CVE cited in the secondary summary).

Caveat — read before claiming full closure

ControlPlane's unauthenticated→RCE chain in Vault/OpenBao (4 chained flaws, incl. Raft snapshot-force) was demonstrated pre-October; OpenBao fixed it in 2.6.3/2.7.0 (23 Sep). The Oct 7 bulletins above are the vendor fixes to apply — don't claim they close every element of that chain without reading HCSEC-2026-41/43 in full. Restrict snapshot-restore and sys/raw/recovery access regardless.

Terraform-relevant: go-getter CVE-2026-19585 (8 Oct)

go-getter < 1.8.10 / v2 < 2.2.5 writes files outside the destination on S3/GCS directory downloads; fixed 1.8.10 / 2.2.5. Terraform module downloads use go-getter — upgrade Terraform/the library, don't just patch Vault. (Earlier go-getter CVE-2026-88922, archive permission bits, fixed 1.8.9/2.2.4 on 15 Sep.)

Sources: HashiCorp Discuss — HCSEC-2026-43 · CoHash — Vault 2.2 · ControlPlane — Vault/OpenBao chain

8 · n8n & Ollama

Both are in your local lab — and Ollama is the one you already had flagged for a CVE fix earlier this month.

n8n — 5 October pair fix: 2.38.2 / 2.37.7 / 1.123.76

Severity: 9.4, sandbox escape. Exploitation: Not known exploited. CVE-2026-86076 — expression sandbox escape (RCE for workflow authors; the patch rejects reserved member names such as __sanitize). CVE-2026-44790 — 9.4, argument injection (CWE-88). Both published 5 Oct, hands-on effort, full control if successful. Hardest on shared/multi-user instances, where any workflow editor is an expression author. Remediation: Back up SQLite/Postgres + N8N_ENCRYPTION_KEY before upgrading — losing the key locks your credentials. n8n Cloud was patched automatically. Still check the older one: Ni8mare, the unauthenticated RCE CVE-2026-21858 (10.0, fixed 1.121.0/1.121.3) — ~60,000 internet instances were still vulnerable in September scans and 76% of exposed instances ran known-critical versions per Censys (Oct 2026). If you're below 1.121.0 you're vulnerable to that too, regardless of the Oct 5 patch.

Source: DEV — CortexFlow

Ollama CVE-2026-103663 — /api/pull path traversal → RCE as root, fix: ≥ 0.35.0

Severity: HIGH–CRITICAL. Exploitation: No known exploitation reported. Verified live. digestToPath does not validate layer digests, so an unauthenticated remote attacker can write a malicious binary outside the model store; in default Docker images the server can write /usr/lib/ollama, which is loaded/executed as root on the next restart. Affected: 0.34.2–0.35.0 (CERT wording; other databases list < 0.35.0). Score discrepancy — reported, not resolved: CERT Polska (primary) gives no CVSS; StackFlag lists 7.8 High; one secondary briefing lists 9.4 Critical. Treat as high-to-critical for an internet-exposed instance either way. Remediation: Upgrade to ≥ 0.35.0 · never expose Ollama unauthenticated to a network (bind loopback / reverse-proxy auth) · audit /usr/lib/ollama and the model store for unexpected files · restart only after the upgrade. Same audit: agent-mode approval bypass CVE-2026-102697 (8.5 CVSS v4.0, 0.14.0–0.31.1, fixed 0.31.2, published 29 Sep) — prefix-only approval lets prompt injection chain commands after ; / &&.

Source: CERT Polska — CVE-2026-103663

9 · Kubernetes, OpenShift, Java/Spring

Includes the compromised-CA story that hits Java, Go and Rundeck at once.

Kubernetes & ecosystem cluster

Severity: 10.0 — Azure K8s Configuration; 9.3 — OpenShift Console; 5.9 / 5.8 — K8s Core.

CVE-2026-83938 · 10.0 — Azure Kubernetes Configuration Service

Missing authentication for a critical function (CWE-306) → unauthenticated privilege escalation over the network. Published 22 Oct; no KEV listing, no public exploit reported, EPSS ~0.7%. No fixed-version list was published in the sources found — this is a Microsoft-managed service; verify in MSRC and Azure service health, restrict exposure, and do not assume a customer patch exists.

CVE-2026-2270 · 5.9 — StatefulSet confused deputy (26 Oct)

Namespace-scoped write on a StatefulSet + ControllerRevision → cross-namespace pod creation with full pod spec control (the pod is GC-deleted unless the attacker forges a valid StatefulSet OwnerReference UID in the victim namespace). Affected: kube-controller-manager ≤ 1.34.11 / ≤ 1.35.8 / ≤ 1.36.4 / = 1.37.0; fixed ≥ 1.34.12 / ≥ 1.35.9 / ≥ 1.36.5 / ≥ 1.37.1.

CVE-2026-76654 · 5.8 — Windows kubelet NTLM coercion

Symlink subPath → UNC share → NetNTLMv2 hash of the kubelet account (crack/relay if domain-joined). Patch Windows nodes; disable symlink subPaths where possible.

Traefik CVE-2026-114636 · Lagoon CVE-2026-113788 · Consul-k8s CVE-2026-110720

Traefik 3.7.13 only: K8s Ingress NGINX provider — ssl-passthrough Ingresses on non-TLS entry points silently skip auth-type/auth-url/whitelist annotations (no logs). Fixed 3.7.14. Lagoon core < 2.33.2: ssh service command injection, authenticated user → platform admin; fixed 2.33.2 / Helm chart 1.62.2. Consul-k8s 0.42.0–2.0.4: ACL binding rules not consistently restricted to the Consul namespace (26 Oct); fixed 1.8.18 / 2.1.0; no CVSS in the bulletin summary found.

OpenShift — console SSRF 9.3, oc-mirror 7.4, oc extract 5.5

CVE-2026-75885 (9.3, Red Hat): console unauthenticated /api/devfile/ + /api/devfile/samples/ → SSRF against internal cluster services + unbounded-memory DoS; OCP 4.12–4.17 console affected — apply the Red Hat advisory for your stream. CVE-2026-75939 (7.4): oc-mirror PGP signature-verification bypass → forged release payload mirrored into disconnected registries; no mitigation meeting Red Hat criteria — upgrade oc-mirror when a fixed build ships and verify release signatures out-of-band. CVE-2026-115424 (5.5, 22 Oct): oc image extract symlink/hardlink sanitisation failure → write outside the extraction dir.

Node kernels — PwnEscape CVE-2026-99999

Linux virtio-net/KVM guest→host escape class, widely covered mid-Oct. Patch node kernels per distro (Azure Linux 3.0 fixed in kernel 6.6.159.1-1, published 16 Oct) and reboot nodes on a rolling schedule — a node fix, not an image fix. AWS bulletin 2026-137 states AWS-managed EC2/Lambda/Fargate are not affected; self-managed KVM/Firecracker hosts on AWS are.

Sources: MSRC — CVE-2026-83938 · oss-sec — K8s · Red Hat — CVE-2026-75885 · Red Hat — CVE-2026-75939 · SentinelOne — Traefik · CyberSecurityNews — Consul-k8s

Java / Spring Boot — the eMudhra compromised-CA event + two Spring items your scanners will flag

Severity: 9.1 — eMudhra CA; 9.8 — Spring (see discrepancy).

CVE-2026-91403 · 9.1 (21 Oct, out-of-band)

Not an RCE bug: Java still trusted 8 eMudhra intermediate CAs whose private keys are computable (one key was literally 4; ≤ 8 bits of entropy) → a MITM can mint valid certificates for any hostname and read/tamper TLS traffic. Affected: Oracle Java SE 8u511, 11.0.33, 17.0.21, 21.0.13, 25.0.5, 27.0.1 (+ GraalVM 17.0.21/21.0.13). Remediation: Apply your vendor's patched JDK; if unpatched, add the intermediates to blocked.certs and/or enable revocation checking as an interim. Follow-up CVE-2026-114820 · 9.1 (24 Oct): the first .0.2 fixed builds are also affected, and the fix also removes 4 eMudhra + Sectigo roots — do not stop at the first patch; re-check your vendor's latest build. The same root cause drove Docker 29.9.2 (Go, CVE-2026-111943) and the Rundeck 6.3.0 images — rebuild all Java/Go base images, not just app code.

Spring Framework CVE-2026-59313 — SSE injection

spring-webmvc functional ServerResponse.sse(...) with attacker-influenced streamed text; annotated controllers with SseEmitter and WebFlux SSE are not affected. Rating discrepancy: the CVE record says 9.8 Critical, Spring's own rating is Low — scanners will flag 9.8. Free OSS fix only in 7.0.9; 6.2 and older lines get the fix only via Broadcom commercial support. Decision point: upgrade to Spring Framework 7 / Spring Boot 4, buy commercial support, or remove SSE functional endpoints.

Spring Framework CVE-2026-47892 · 9.8 — WebFlux CORS pre-flight bypass

The WebFlux functional router skips header-based predicate checks on CORS pre-flight (OPTIONS) when deployed via DispatcherServlet → unauthenticated bypass of header-enforced access controls. Fixed: 5.2.26.RELEASE / 5.3.50 / 6.0.31 / 6.1.29 / 6.2.20 / 7.0.8.1; Spring Boot users override spring-framework.version. Workaround: move header enforcement out of functional predicates (e.g. the Spring Security filter chain). Published 27 Aug, record updated 26 Oct — included because scanners surfaced it in this window.

Spring Boot Actuator-prefix auth bypasses CVE-2026-22731 / CVE-2026-22733 · 8.2

Endpoints under a custom Actuator health-group path (e.g. /healthz/admin) or the CloudFoundry path (/cloudfoundryapplication) inherit the Actuator's permissive policy → unauthenticated access. Check your Actuator path mappings and upgrade Spring Boot per the vendor advisory for your line. Open item: the secondary source gives CVSS and mechanism but no per-line fixed-version table — confirm in the Spring Boot advisory before upgrading.

Sources: CyberSecurityNews — Java update · bnowakowski — CVE-2026-91403 PSA · HeroDevs — 59313 · Armis — 47892

10 · Azure/AWS, Nexus, Rundeck

Most Azure items are Microsoft-managed — “no customer action” still means verify in MSRC.

Azure October Patch Tuesday (13 Oct) + 22 Oct batch

Severity: 9.8–10.0 cluster. Status: Microsoft-managed — check MSRC. CVE-2026-77900 · 9.8 — Azure App Service for Linux: missing authentication → unauthenticated RCE. Microsoft lists it as no customer action required (service-side fix) in Patch Tuesday summaries — verify your App Service is on a current runtime and audit for unexpected processes/endpoints; do not treat “no action” as “no exposure window”. (The MSRC page itself was not re-read live in this research.) Other 22 Oct Microsoft 10.0/9.9 cloud CVEs, all described as Microsoft-managed (check MSRC for customer action): CVE-2026-97785 (10.0, AAD App Proxy improper authentication → privilege escalation) · CVE-2026-110063 (10.0, VM Inspector for Azure VMs link-following → privilege escalation) · CVE-2026-105014 (Azure Pipelines command injection — directly relevant to your pipelines; check agent/task versions and MSRC) · CVE-2026-96207 (10.0, Partner Center) · CVE-2026-94510 (9.9, Bookings) · CVE-2026-88131 (9.8, Dataverse RCE) · CVE-2026-69435 (9.6, Azure SRE Agent). Plus CVE-2026-96204 (Azure Arc Connected Machine Agent, Important): local IMDS SSRF → SYSTEM via the extension manager — patch Arc agents and audit Microsoft.HybridCompute/machines/extensions/write by the machine's own managed identity. AWS: no AWS-service CVE of comparable severity was found in the window.

Sources: BleepingComputer — Patch Tuesday · MSRC — CVE-2026-77900

Sonatype Nexus Repository 3 — 15 Oct auth-bypass cluster, fix: ≥ 3.97.0

Severity: 8.7 top item. No RCE, but worth the same window: CVE-2026-97712 · 8.7 role escalation · CVE-2026-97665 · 7.1 NuGet .snupkg upload auth bypass (read+browse only, incl. anonymous by default if enabled, when Symbol Server is enabled) · CVE-2026-97666 · 6.9 unauthenticated ExtDirect input to a Java class-loading call (a class-presence oracle, not RCE by itself) · CVE-2026-97667 · 5.1 stored XSS via R-package metadata (≥ 3.28.0 < 3.97.0). Remediation: Upgrade Nexus Repository 3 to ≥ 3.97.0 — that's the fixed bound stated for 97667; confirm the same build closes 97712/97665/97666 in Sonatype's release notes for your stream. Disable anonymous access and Symbol Server if unused. SonarQube, Fortify, Wiz: no critical/high product CVE for any of the three was found in the late-Sep–Oct 27 searches. SonarQube's October news was a Remediation Agent feature, not a security advisory. That's absence of evidence in these searches, not a clean bill — re-run a vendor-advisory check before closing them out.

Sources: Sonatype Guide · StackWatch — Sonatype

Rundeck CVE-2026-106056 — OS command injection on Windows nodes, fix: 6.2.0 · use 6.3.0 images

Severity: 7.7 HIGH. Rundeck < 6.2.0: an authenticated user with job-run permission injects via free-text options — CLIUtils.quoteWindowsCMDArg single-quotes are ineffective against cmd.exe. Fixed: 6.2.0. Remediation until patched: restrict job-run permission and free-text options on Windows nodes. Separately, Rundeck/Runbook Automation 6.3.0 (22 Oct, early release) ships patched official Docker images for the compromised eMudhra intermediate CA — pull 6.3.0 images rather than rebuilding on older tags.

Sources: OpenCVE — CVE-2026-106056 · Rundeck release notes

11 · CISA KEV — actively exploited, October additions

Check for these even if they're outside your named stack. Federal deadlines shown where the research recorded them.

AddedItemDetail
23 OctCVE-2026-110447 · 9.2 — Collabora Online CODE ServerCommand injection via proxy.php; attackers backdooring Nextcloud servers, hunting credential stores/wallets. Fixed 26.4.303. KEV, federal deadline 26 Oct.
23 OctCVE-2026-114625 · 9.9 — Arista VeloCloud OrchestratorSQL injection, authenticated Enterprise Read-Only minimum. Fixed 5.2.3.18 / 6.1.3.10 / 6.4.2.12. KEV, federal deadline 26 Oct.
20 OctPlex Media Server ×4 — CVE-2026-96652/96654/96655/96656 (“Plex2Shell”)Plus SiteGuard CVE-2026-106931 and Siemens Software Center CVE-2026-110936, all added to KEV.
14 OctCVE-2026-0322 PAN-OS · CVE-2026-76519 Cisco Secure Email Gateway · CVE-2026-105133/105134 Ahsay AhsayCBSCode injection; path traversal; improper auth + command injection. Federal deadline 17 Oct.
4–8 OctCitrix NetScaler CVE-2026-88779 + older exploited flawsProFTPD CVE-2015-3306, BIND CVE-2015-5477, Struts CVE-2016-3081, ONLYOFFICE CVE-2021-3199, Strapi CVE-2023-22894 — if any of these still run in your estate, they are being exploited now regardless of age.

Sources: Kargin-Utkin — KEV additions · Neoteo — KEV catalog · TechUpscale — week of Oct 20–26. Note: no DevOps-stack item above was confirmed KEV at research time — the supply-chain compromises in §1 are confirmed malicious shipments, which outrank KEV status. Several non-KEV items had public PoCs within days of disclosure.

12 · End-of-life exposure

Imminent or just passed — no patch will come for these lines.

Runtime / lineEOL dateConsequence in this window
Python 3.101 Oct 2026No more security fixes; 3.11 is supported to Oct 2027. Any 3.10 image is now permanently unpatched.
Node.js 20 and older30 Apr 2026No fix for Node2Shell or any Oct 2026 Node flaw; move to 22 (EOL 30 Apr 2027) or 24/26 — 26 becomes LTS 28 Oct 2026 (EOL 30 Apr 2029). Node 24 entered Maintenance 20 Oct 2026.
.NET 8 (LTS) + .NET 9 (STS)10 Nov 2026 — imminentBoth lose patches the same day; target .NET 10 LTS now, before the deadline.
Oracle JDK 17 Premier SupportSep 2026Oracle premier support ended; Adoptium/Microsoft/Amazon builds have different dates — record vendor + build, don't assume coverage.
Windows Server 2012 / 2012 R213 Oct 2026Final year of paid Extended Security Updates ended.
Argo CD 2.x, 3.0–3.2Already unsupportedNo patch for the Oct 7 9.9s — upgrade is the only fix.
Spring Framework ≤ 6.2 / Spring Boot 3.5-era linesCommercial-only fixesFree OSS fix for CVE-2026-59313 only on 7.0.x; budget for the upgrade or Broadcom support.
Jenkins LTS older than 2.568.3 / plugins in §6—Update-center warnings stop being theoretical once the advisory ships.

Source: Safeguard — runtime EOL calendar 2026–2027

Already on your radar

Covered in your Oct 27 briefing — one line each, not repeated in detail.

  • PHP2Shell — PHP, 21 Oct: CVE-2026-109618 (9.8) and companion flaw; fixed 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12.
  • Node2Shell — Node.js Brotli CVE-2026-111497 (9.2 CVSS v4.0; Node rates it High); fixed 22.23.4 / 24.22.1 / 26.12.1 on 21 Oct; PoC demonstrated 22 Oct; no confirmed in-the-wild exploitation as of 26 Oct; Node 20 and older are EOL and unpatched.
  • GitLab CI/CD regex flaw — CVE-2026-97229 (10.0, 12 Oct; fixed 19.2.8 / 19.3.5 / 19.4.2) and sibling org-mode regex flaw CVE-2026-110264 (10.0, 21 Oct; fixed 18.11.13 / 19.0.10 / 19.1.9 / 19.2.9 / 19.3.6 / 19.4.3 / 19.5.1; no workaround; GitLab.com/Dedicated already patched).

Open questions — could not verify

Stated plainly rather than guessed.

  • Podman, Fortify, SonarQube, Wiz, Terraform core (beyond go-getter): no critical/high CVE found in late-Sep–Oct 27 searches — absence of evidence in these searches, not proof of no vulnerability. Re-check vendor advisories before closing.
  • Azure CVE-2026-83938, 97785, 110063, 105014 fixed versions: Microsoft-managed services; sources give no customer-installable fixed version. Confirm in MSRC / Azure service health rather than assuming “no action”.
  • Spring CVE-2026-22731/22733: no per-line fixed-version table in the source used — confirm in the Spring Boot advisory.
  • Nexus: 3.97.0 is the stated fixed bound for CVE-2026-97667 — confirm the same build closes 97712/97665/97666 in Sonatype's release notes.
  • Ollama CVE-2026-103663 CVSS: sources disagree (7.8 vs 9.4); CERT Polska, the primary source, gives no score.
  • Spring CVE-2026-59313: CVE record 9.8 vs Spring's own Low rating — discrepancy reported, not resolved.

Snapshot: research compiled 27 October 2026, covering disclosures from late September to 27 October 2026. This page is fixed at build time — versions, KEV additions and EOL dates will move; re-check the linked vendor advisories before a patch window.

Primary / verified-live sources: Jenkins advisory 2026-09-02 · Docker Engine 29 release notes · CERT Polska — Ollama · Microsoft — LiteLLM compromise. All other items rest on the secondary sources linked in each card.