VYPR
High severityOSV Advisory· Published Jul 30, 2025· Updated Apr 15, 2026

CVE-2025-54433

CVE-2025-54433

Description

Bugsink is a self-hosted error tracking service. In versions 1.4.2 and below, 1.5.0 through 1.5.4, 1.6.0 through 1.6.3, and 1.7.0 through 1.7.3, ingestion paths construct file locations directly from untrusted event_id input without validation. A specially crafted event_id can result in paths outside the intended directory, potentially allowing file overwrite or creation in arbitrary locations. Submitting such input requires access to a valid DSN, potentially exposing them. If Bugsink runs in a container, the effect is confined to the container’s filesystem. In non-containerized setups, the overwrite may affect other parts of the system accessible to that user. This is fixed in versions 1.4.3, 1.5.5, 1.6.4 and 1.7.4.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
bugsinkPyPI
>= 1.7.0, < 1.7.41.7.4
bugsinkPyPI
>= 1.6.0, < 1.6.41.6.4
bugsinkPyPI
>= 1.5.0, < 1.5.51.5.5
bugsinkPyPI
< 1.4.31.4.3

Affected products

1

Patches

12
b94aa8a5c96c

envelope event_id check: on-parse

https://github.com/bugsink/bugsinkKlaas van SchelvenJul 29, 2025via ghsa
2 files changed · +12 1
  • ingest/filestore.py+4 1 modified
    @@ -8,7 +8,10 @@ def get_filename_for_event_id(event_id):
         # implemented. However, counterpoint: when doing stress tests, it was quite hard to get a serious backlog going
         # (snappea was very well able to play catch-up). So this might not be necessary.
     
    -    # ensure that event_id is a uuid, and remove dashes if present
    +    # ensure that event_id is a uuid, and remove dashes if present; also doubles as a security-check (event_id is
    +    # user-provided (but at this point already validated to be a valid UUID), but b/c of the below the
    +    # security-implications of os.path.join can be understood right here in the code without needing to inspect all
    +    # call-sites).
         event_id_normalized = uuid.UUID(event_id).hex
     
         return os.path.join(get_settings().INGEST_STORE_BASE_DIR, event_id_normalized)
    
  • ingest/views.py+8 0 modified
    @@ -1,3 +1,4 @@
    +import uuid
     import hashlib
     import os
     import logging
    @@ -618,6 +619,13 @@ def factory(item_headers):
                     # payload's event_id), so we can rely on it having been set.
                     if "event_id" not in envelope_headers:
                         raise ParseError("event_id not found in envelope headers")
    +
    +                try:
    +                    # validate that the event_id is a valid UUID as per the spec (validate at the edge)
    +                    uuid.UUID(envelope_headers["event_id"])
    +                except ValueError:
    +                    raise ParseError("event_id in envelope headers is not a valid UUID")
    +
                     filename = get_filename_for_event_id(envelope_headers["event_id"])
                     os.makedirs(os.path.dirname(filename), exist_ok=True)
                     return MaxDataWriter("MAX_EVENT_SIZE", open(filename, 'wb'))
    
c341687bd655

envelope event_id check: on-parse

https://github.com/bugsink/bugsinkKlaas van SchelvenJul 29, 2025via ghsa
2 files changed · +12 1
  • ingest/filestore.py+4 1 modified
    @@ -8,7 +8,10 @@ def get_filename_for_event_id(event_id):
         # implemented. However, counterpoint: when doing stress tests, it was quite hard to get a serious backlog going
         # (snappea was very well able to play catch-up). So this might not be necessary.
     
    -    # ensure that event_id is a uuid, and remove dashes if present
    +    # ensure that event_id is a uuid, and remove dashes if present; also doubles as a security-check (event_id is
    +    # user-provided (but at this point already validated to be a valid UUID), but b/c of the below the
    +    # security-implications of os.path.join can be understood right here in the code without needing to inspect all
    +    # call-sites).
         event_id_normalized = uuid.UUID(event_id).hex
     
         return os.path.join(get_settings().INGEST_STORE_BASE_DIR, event_id_normalized)
    
  • ingest/views.py+8 0 modified
    @@ -1,3 +1,4 @@
    +import uuid
     import hashlib
     import os
     import logging
    @@ -611,6 +612,13 @@ def factory(item_headers):
                     # payload's event_id), so we can rely on it having been set.
                     if "event_id" not in envelope_headers:
                         raise ParseError("event_id not found in envelope headers")
    +
    +                try:
    +                    # validate that the event_id is a valid UUID as per the spec (validate at the edge)
    +                    uuid.UUID(envelope_headers["event_id"])
    +                except ValueError:
    +                    raise ParseError("event_id in envelope headers is not a valid UUID")
    +
                     filename = get_filename_for_event_id(envelope_headers["event_id"])
                     os.makedirs(os.path.dirname(filename), exist_ok=True)
                     return MaxDataWriter("MAX_EVENT_SIZE", open(filename, 'wb'))
    
211ddf76758c

envelope event_id check: on-parse

https://github.com/bugsink/bugsinkKlaas van SchelvenJul 29, 2025via ghsa
2 files changed · +12 1
  • ingest/filestore.py+4 1 modified
    @@ -8,7 +8,10 @@ def get_filename_for_event_id(event_id):
         # implemented. However, counterpoint: when doing stress tests, it was quite hard to get a serious backlog going
         # (snappea was very well able to play catch-up). So this might not be necessary.
     
    -    # ensure that event_id is a uuid, and remove dashes if present
    +    # ensure that event_id is a uuid, and remove dashes if present; also doubles as a security-check (event_id is
    +    # user-provided (but at this point already validated to be a valid UUID), but b/c of the below the
    +    # security-implications of os.path.join can be understood right here in the code without needing to inspect all
    +    # call-sites).
         event_id_normalized = uuid.UUID(event_id).hex
     
         return os.path.join(get_settings().INGEST_STORE_BASE_DIR, event_id_normalized)
    
  • ingest/views.py+8 0 modified
    @@ -1,3 +1,4 @@
    +import uuid
     import os
     import logging
     import io
    @@ -602,6 +603,13 @@ def factory(item_headers):
                     # payload's event_id), so we can rely on it having been set.
                     if "event_id" not in envelope_headers:
                         raise ParseError("event_id not found in envelope headers")
    +
    +                try:
    +                    # validate that the event_id is a valid UUID as per the spec (validate at the edge)
    +                    uuid.UUID(envelope_headers["event_id"])
    +                except ValueError:
    +                    raise ParseError("event_id in envelope headers is not a valid UUID")
    +
                     filename = get_filename_for_event_id(envelope_headers["event_id"])
                     os.makedirs(os.path.dirname(filename), exist_ok=True)
                     return MaxDataWriter("MAX_EVENT_SIZE", open(filename, 'wb'))
    
c87217bd5651

envelope event_id check: on-parse

https://github.com/bugsink/bugsinkKlaas van SchelvenJul 29, 2025via ghsa
2 files changed · +12 1
  • ingest/filestore.py+4 1 modified
    @@ -8,7 +8,10 @@ def get_filename_for_event_id(event_id):
         # implemented. However, counterpoint: when doing stress tests, it was quite hard to get a serious backlog going
         # (snappea was very well able to play catch-up). So this might not be necessary.
     
    -    # ensure that event_id is a uuid, and remove dashes if present
    +    # ensure that event_id is a uuid, and remove dashes if present; also doubles as a security-check (event_id is
    +    # user-provided (but at this point already validated to be a valid UUID), but b/c of the below the
    +    # security-implications of os.path.join can be understood right here in the code without needing to inspect all
    +    # call-sites).
         event_id_normalized = uuid.UUID(event_id).hex
     
         return os.path.join(get_settings().INGEST_STORE_BASE_DIR, event_id_normalized)
    
  • ingest/views.py+8 0 modified
    @@ -1,3 +1,4 @@
    +import uuid
     import hashlib
     import os
     import logging
    @@ -611,6 +612,13 @@ def factory(item_headers):
                     # payload's event_id), so we can rely on it having been set.
                     if "event_id" not in envelope_headers:
                         raise ParseError("event_id not found in envelope headers")
    +
    +                try:
    +                    # validate that the event_id is a valid UUID as per the spec (validate at the edge)
    +                    uuid.UUID(envelope_headers["event_id"])
    +                except ValueError:
    +                    raise ParseError("event_id in envelope headers is not a valid UUID")
    +
                     filename = get_filename_for_event_id(envelope_headers["event_id"])
                     os.makedirs(os.path.dirname(filename), exist_ok=True)
                     return MaxDataWriter("MAX_EVENT_SIZE", open(filename, 'wb'))
    
1001726f4389

Ingestion: ensure event_id is a dashless uuid before using as a filename

https://github.com/bugsink/bugsinkKlaas van SchelvenJul 29, 2025via ghsa
1 file changed · +5 1
  • ingest/filestore.py+5 1 modified
    @@ -1,3 +1,4 @@
    +import uuid
     import os
     from bugsink.app_settings import get_settings
     
    @@ -7,4 +8,7 @@ def get_filename_for_event_id(event_id):
         # implemented. However, counterpoint: when doing stress tests, it was quite hard to get a serious backlog going
         # (snappea was very well able to play catch-up). So this might not be necessary.
     
    -    return os.path.join(get_settings().INGEST_STORE_BASE_DIR, event_id)
    +    # ensure that event_id is a uuid, and remove dashes if present
    +    event_id_normalized = uuid.UUID(event_id).hex
    +
    +    return os.path.join(get_settings().INGEST_STORE_BASE_DIR, event_id_normalized)
    
2c41fbe3881b

Ingestion: ensure event_id is a dashless uuid before using as a filename

https://github.com/bugsink/bugsinkKlaas van SchelvenJul 29, 2025via ghsa
1 file changed · +5 1
  • ingest/filestore.py+5 1 modified
    @@ -1,3 +1,4 @@
    +import uuid
     import os
     from bugsink.app_settings import get_settings
     
    @@ -7,4 +8,7 @@ def get_filename_for_event_id(event_id):
         # implemented. However, counterpoint: when doing stress tests, it was quite hard to get a serious backlog going
         # (snappea was very well able to play catch-up). So this might not be necessary.
     
    -    return os.path.join(get_settings().INGEST_STORE_BASE_DIR, event_id)
    +    # ensure that event_id is a uuid, and remove dashes if present
    +    event_id_normalized = uuid.UUID(event_id).hex
    +
    +    return os.path.join(get_settings().INGEST_STORE_BASE_DIR, event_id_normalized)
    
53cf1a17a3e9

Ingestion: ensure event_id is a dashless uuid before using as a filename

https://github.com/bugsink/bugsinkKlaas van SchelvenJul 29, 2025via ghsa
1 file changed · +5 1
  • ingest/filestore.py+5 1 modified
    @@ -1,3 +1,4 @@
    +import uuid
     import os
     from bugsink.app_settings import get_settings
     
    @@ -7,4 +8,7 @@ def get_filename_for_event_id(event_id):
         # implemented. However, counterpoint: when doing stress tests, it was quite hard to get a serious backlog going
         # (snappea was very well able to play catch-up). So this might not be necessary.
     
    -    return os.path.join(get_settings().INGEST_STORE_BASE_DIR, event_id)
    +    # ensure that event_id is a uuid, and remove dashes if present
    +    event_id_normalized = uuid.UUID(event_id).hex
    +
    +    return os.path.join(get_settings().INGEST_STORE_BASE_DIR, event_id_normalized)
    
55a155003d0b

Ingestion: ensure event_id is a dashless uuid before using as a filename

https://github.com/bugsink/bugsinkKlaas van SchelvenJul 29, 2025via ghsa
1 file changed · +5 1
  • ingest/filestore.py+5 1 modified
    @@ -1,3 +1,4 @@
    +import uuid
     import os
     from bugsink.app_settings import get_settings
     
    @@ -7,4 +8,7 @@ def get_filename_for_event_id(event_id):
         # implemented. However, counterpoint: when doing stress tests, it was quite hard to get a serious backlog going
         # (snappea was very well able to play catch-up). So this might not be necessary.
     
    -    return os.path.join(get_settings().INGEST_STORE_BASE_DIR, event_id)
    +    # ensure that event_id is a uuid, and remove dashes if present
    +    event_id_normalized = uuid.UUID(event_id).hex
    +
    +    return os.path.join(get_settings().INGEST_STORE_BASE_DIR, event_id_normalized)
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

11

News mentions

0

No linked articles in our index yet.