- Drop 'or PNG' fallback: img.format is always set after a successful
PillowImage.open() call; UnidentifiedImageError fires before returning None.
- Add extension/content consistency check using PillowImage.MIME to catch
mismatches like PNG data with a .jpg extension (content-type drift).
- AVIF/JPEG2000: raise a specific ValidationError when save() fails due to
a missing native codec library, replacing the generic 'unable to process'
message that was a silent regression from the previous store-as-is behaviour.
- Animated WebP: extend the ImageSequence frame-preservation path to WebP
(n_frames > 1) alongside GIF.
- Hoist ImageSequence import to module level; split PIL imports onto separate
lines per ruff isort requirement.
- Tests: use img.n_frames instead of manual seek loop; add animated WebP test;
add comment explaining why test_clean_rejects_disallowed_extension calls
clean() directly rather than full_clean().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move extension allowlist check before PillowImage.open() so SVG/unknown
formats get a clear error rather than a generic 'unable to process' message.
- Replace hasattr(self.image, 'file') guard with not self.image._committed so
already-stored images are not unnecessarily re-encoded on every save.
- Replace bare BytesIO assignment with ContentFile so Django's pre_save()
reliably writes the sanitised content to storage.
- Tighten except clause to (OSError, UnidentifiedImageError, DecompressionBombError).
- Use ImageSequence.Iterator for animated GIF re-encoding to preserve all
frames; pass duration/loop metadata through.
- Add four model tests covering polyglot stripping, animated GIF frame
preservation, disallowed extension rejection, and committed-image skip.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ruff I001: PIL was in an isolated block between stdlib and django.
Move it into the same third-party block, alphabetically between
django and rest_framework (d < p < r).
Re-encode uploaded images through Pillow before storage. Pillow's
image.save() produces clean output containing only the image data; any
non-image trailer bytes embedded by a polyglot payload are stripped. An
explicit extension check against IMAGE_ATTACHMENT_IMAGE_FORMATS is also
added so the API path cannot bypass the form-level accept= filter.
Note: X-Content-Type-Options: nosniff and Content-Disposition: attachment
are already provided by the MediaView hardening in #22400 (commit 87c53aa).
This commit provides the complementary upload-time defence.
Pillow is already a required dependency (ImageField uses it for dimension
extraction), so the top-level import adds no new dependency.
Ref: SR-001 / VM-326 (internal security review)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add GET handler to TableConfigEditView that redirects users to home with
a warning if they attempt to access the create form directly without
required object_type and table parameters from a source list view.
Fixes#22237
Introduce declarative GraphQL filter test framework with
`GraphQLFilterTest` and `GraphQLQueryTest` dataclasses. Implement
auto-filter discovery from filter class annotations with per-field-kind
test generators for string, numeric, date, range, and array lookups.
Fixes#15569
Introduce ChoiceSetField as ArrayField subclass for custom field
choices and implement choice_value lookup to filter by value element
only. Update GraphQL filter to use ExtraChoicesLookup with contains and
length options.
Fixes#22324
Repair stale `_path` references when an endpoint instance is cabled but
has no path set, as occurs during cable creation before path tracing.
The `path` accessor now refreshes the denormalized FK from the database
in this case, ensuring event payloads include connected endpoints.
Fixes#21338
* fix(ipam): Honor filters for child availability views
Retain the instantiated child FilterSet on ObjectChildrenView and expose
whether child object filters are active. Use this in IPAM child views to
avoid rendering synthetic availability rows when the child queryset has
been filtered.
This ensures Saved Filters and direct filters are respected on
Prefix IP Address, Child Prefix, Aggregate Prefix,
and VLAN Group VLAN tabs.
Fixes#22210
* refactor(ipam): Replace has_active_filters with ChildAvailabilityMixin
Extracts filter detection logic from ObjectChildrenView into a dedicated
ChildAvailabilityMixin. Compares WHERE clause signatures between
filtered and unfiltered querysets instead of inspecting filter
parameters, improving reliability when child querysets are pre-scoped
to parent objects.
ScriptFileForm only validated uploaded scripts, so a script added by
selecting a data file bypassed validate_script_content. A script that
failed to load was committed as a broken module that showed as missing
and could not be corrected, since re-adding it tripped the file path
uniqueness constraint.
Validate the data file's content in the form's clean() the same way
uploaded files are validated, so a script that cannot be loaded is
rejected before any module is created.
When no ConfigRevision exists, the empty state was never cached, so every
request re-queried core_configrevision. Distinguish a genuine cache miss from
a cached-empty state via a sentinel, seed the empty state on first load, and
only consult the database on a true miss.
Treat the cache as warm only when both 'config' and 'config_version' are
present. A missing 'config_version' (evicted or never written) now re-queries
the database instead of leaving Config.version as None when a ConfigRevision
exists. The no-revision branch writes both keys, so the intentional empty
state remains a cache hit.
The config tests shared a single Redis instance (keyed only by a static
prefix) across parallel test workers, so a no-revision test in one worker
could seed empty config/config_version keys that another worker's test then
read, causing intermittent failures. Use a per-process LocMemCache so the
shared cache keys cannot be contaminated across workers.
Add RQQueueTestMixin to centralize RQ queue cleanup for test cases that
interact with background jobs. The mixin clears all RQ queues in setUp()
and tearDown(), preventing jobs created by one test from leaking into
later unrelated test runs.
Replace duplicate queue cleanup logic in core and netbox tests with the
shared mixin for better maintainability.
Fixes#22318
Rename `CablePathTestCase` to `BaseCablePathTestCase` and
`JobRunnerTestCase` to `BaseJobRunnerTestCase` to clearly indicate
their role as abstract base classes rather than concrete test cases.
Fixes#22338