-
Bug
-
Resolution: Unresolved
-
Major
-
None
-
quay-v3.17.0
Problem Statement
The immutability policy system allows creating contradictory policies for the same tag pattern, leading to undefined behavior.
Users can create two policies for the same tag pattern (e.g., stable):
- Policy 1: Tags NOT matching stable are immutable
- Policy 2: Tags matching stable are immutable
This creates undefined behavior where the system does not know which policy to enforce.

Visual Evidence
See attached screenshot showing repository quayqe/demo with:
- Policy 1: Tag Pattern = stable, Behavior = Tags NOT matching pattern are immutable
- Policy 2: Tag Pattern = stable, Behavior = Tags matching pattern are immutable
Both policies active simultaneously, creating logical contradiction.
Root Cause
File: data/model/immutability.py
Function: _is_duplicate_repository_policy()
The validation checks BOTH tag_pattern AND tag_pattern_matches values. It only rejects duplicates when both fields match exactly.
In the screenshot:
- Policy 1: pattern=stable, matches=False
- Policy 2: pattern=stable, matches=True
Validation passes because matches values differ, even though patterns are identical. This allows contradictory policies.
Correct Behavior
Validation should reject policies with same pattern regardless of matches value. Only ONE policy should be allowed per tag pattern to prevent logical contradictions.
Impact
Severity: MAJOR
Undefined Behavior
- For tag stable: Which policy wins? Immutable or not?
- For tag latest: Which policy wins? Immutable or not?
- Behavior depends on implementation details not guaranteed across versions
Data Integrity
- Database contains contradictory rules
- Policy evaluation is non-deterministic
- Users cannot predict tag immutability status
Compliance Risk
- Cannot guarantee which images are truly immutable
- Violates compliance guarantees for audit trails
Steps to Reproduce
- Navigate to repository
- Go to Settings → Immutability Policies
- Create Policy 1: pattern=stable, behavior=NOT matching are immutable
- Create Policy 2: pattern=stable, behavior=matching are immutable
- Observe: Both policies created successfully (should fail!)
Expected: Error message stating policy for pattern already exists
Actual: Both policies allowed, creating contradiction
Recommended Fix
Remove tag_pattern_matches comparison from duplicate validation.
Change validation to only check tag_pattern field, preventing any two policies with same pattern regardless of matches value.
This ensures logical consistency - one pattern, one policy, one behavior.
Database Query to Find Affected Instances
Find repositories with contradictory policies:
SELECT n.username, r.name, p1.policy->'tag_pattern' as pattern, COUNT(*) as conflicting_count FROM repositoryimmutabilitypolicy p1 JOIN repositoryimmutabilitypolicy p2 ON p1.repository = p2.repository AND p1.uuid < p2.uuid AND p1.policy->'tag_pattern' = p2.policy->'tag_pattern' AND p1.policy->'tag_pattern_matches' != p2.policy->'tag_pattern_matches' JOIN repository r ON p1.repository = r.id JOIN namespace n ON r.namespace_user = n.id GROUP BY n.username, r.name, pattern;
Acceptance Criteria
- System rejects policies with same tag_pattern but different tag_pattern_matches
- Error message clearly states pattern already exists
- Updating existing policy still works
- Unit tests verify contradictory policies rejected
- No contradictory policies exist in production after cleanup
Reported by: Luffy Zhang (lzha1981)
Generated by: Claude Code Automated Bug Analysis