feat(policy): deny security groups by name in the example policy - #23
Merged
Conversation
The example hides a security-internal bug through two rules that are both incidental to "security": group-restricted (any group at all) and undisclosed-marker (an "embargo" substring in the title). Its own comment invites the operator to narrow the broad rule to specific group names — but an operator who did so, or who dropped it to make partner-confidential bugs visible, was left with no rule naming the security groups, and a security-group bug with an unmarked title would be served in full. Add a dedicated "security-groups" deny rule matching group names by glob (*security*, *embargo*, *internal*) directly below the broad rule: redundant while that rule keeps its full breadth, load-bearing the moment it is narrowed or removed. Update the header scenario and the narrowing advice accordingly. The rule can only deny more, never grant, and because it consults `groups` — unknowable for a not-yet-created bug — the file's refuse-all-creation property holds even without the broad rule (I4 fail-closed). The new test pins the shipped file: a bug in a security-named group carrying no other deniable signal is denied; it stays denied by "security-groups" itself once the group-restricted rule is excised from the policy text; and a restricted non-security bug does become visible then, which is exactly what narrowing is for. Mutation checked: deleting the security-groups rule from the text makes the test fail.
plusky
force-pushed
the
feat/policy-security-group-default
branch
from
July 28, 2026 08:57
daf66d5 to
8df47a5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a dedicated
security-groupsdeny rule toexamples/policy.toml, matching group names by glob (*security*,*embargo*,*internal*), placed directly below the broadgroup-restrictedrule, plus a test pinning the shipped file's behaviour. Header scenario and the broad rule's narrowing advice updated to match.Why
The example hid a security-internal bug only through rules incidental to "security":
group-restricted(matches ANY group) andundisclosed-marker(an "embargo" substring in the title). The file's own comment invites operators to narrow the broad rule to their instance's group names — and an operator who followed that advice, or dropped the rule to make partner-confidential bugs visible, ended up with no rule naming the security groups at all. A security-group bug whose title carries no marker would then be served in full.The new rule is redundant while
group-restrictedkeeps its full breadth (first match wins, so it is simply never reached) and becomes load-bearing the moment that rule is narrowed or removed. The three stems cover the common naming families for sensitive groups — the security team's own groups, embargo-state groups, and internality-named groups — and the comment tells operators that group vocabularies are per-instance and to check theirs. Over-matching only hides more (safe direction); a missed name is what the broad rule exists to catch.Adversarial review against DESIGN.md invariants
action = "deny"with no capabilities: it can only remove access, never grant it, so it cannot weaken any existing guarantee.groups, which is unknowable for a not-yet-created bug, so the file's documented refuse-all-creation property now survives removal of the broad rule too (I4 fail-closed, unknown-metadata tri-state).any-glob-anygroup matching, first-match-wins, and Unknown⇒deny already behave as required; this is defence in depth at the shipped-default layer only.Considered but not changed: security products at any age
Under a narrowed rule 1, a world-readable bug in a security-named product is visible unless a
fresh-security-*rule holds it back — and those rules match keywords and whiteboard tags, not products, so an unlabelled one is visible at any age (this was already true before this change). I deliberately did not add a product-based always-deny: the example's stated scenario is that security-labelled bugs become visible after their freshness window, and a bug that is world-readable on the instance, unmarked, unkeyworded, and past the window is disclosed by the instance itself — hiding it forever would contradict the file's documented 5-day disclosure model without adding confidentiality. The dangerous case (an incident that must stay hidden) is characterised precisely by its group restriction, which is what the new rule pins by name. Operators with a stricter stance can add aproducts = ["*security*"]deny themselves.Verification
shipped_example_policy_denies_security_named_groups_on_their_own: a bug in a security-named group with no other deniable signal (no marker, no keywords, empty whiteboard, years old) is denied by the shipped file; after excising the wholegroup-restrictedrule from the policy text it is still denied, andAccess::Denied { rule }provessecurity-groupsitself decides; a restricted non-security bug becomes visible under the narrowed policy, which is what narrowing is for.security-groupsrule additionally deleted from the policy text, that test fails ("removing the broad rule must not expose security-group bugs").cargo test --workspace --locked(195 passed),cargo clippy --workspace --all-targets --locked -- -D warnings,cargo fmt --check,typos— all clean.