Skip to content

ArtifactFileField.pre_save spuriously rejects uploads named 'artifact*' when MEDIA_ROOT is empty #8041

Description

@dralley

Summary

ArtifactFileField.pre_save uses a fragile prefix check to decide whether an incoming file is already inside artifact storage:

https://github.com/pulp/pulpcore/blob/main/pulpcore/app/models/fields.py#L69-L73

is_in_artifact_storage = file.name.startswith(os.path.join(settings.MEDIA_ROOT, "artifact"))

When settings.MEDIA_ROOT is "" — which is the case for object-storage backends (azure, s3, etc.) — this collapses to:

file.name.startswith("artifact")

file.name for a fresh upload is the client-provided filename. Any uploaded file whose name begins with the substring artifact (e.g. artifact-foo-1.0-1.noarch.rpm) therefore matches, already_in_place is False, and pre_save raises:

ValueError: The file referenced by the Artifact is already present in Artifact storage.
Files must be stored outside this location prior to Artifact creation.

which surfaces to the client as a 500 Internal Server Error on POST /pulp/<domain>/api/v3/artifacts/.

Impact

Any user uploading an artifact whose filename starts with artifact to a deployment with an empty MEDIA_ROOT (object storage) gets a spurious 500. The bug is filename-dependent and backend-dependent, so it is easy to miss.

How it was discovered

A pulp_rpm functional test (test_domains.py::test_artifact_from_file) built an RPM named artifact-<hex>-1.0-1.noarch.rpm and uploaded it via the artifacts endpoint. It failed only on the azure job (empty MEDIA_ROOT); the sibling test using a content- prefix passed.

Suggested fix

The prefix check is meant to detect files that are already physically in artifact storage. It should not rely on a bare startswith against a possibly-empty settings.MEDIA_ROOT, and it arguably should consider the current domain's actual storage location rather than the global setting. Options:

  • Normalize/join paths and compare path components instead of doing a raw string prefix match.
  • Guard against an empty MEDIA_ROOT so the check does not degenerate to startswith("artifact").

Environment

  • Reproducible with any object-storage backend where MEDIA_ROOT is empty (azure/s3), including per-domain FileSystem storage where the global settings.MEDIA_ROOT is still empty.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions