fix(git): fail closed on YAML anchors in tea token fallback (#865 round 12)
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
The PyYAML-absent fallback's _strip_properties consumed a plain anchor
('&name') per-scalar, with no document-scoped registry of declared anchor
names. Real PyYAML's Composer rejects a duplicate anchor NAME declared
anywhere in the document (ComposerError, whole-document fail), but the
fallback would still emit the later token: fail-open (HIGH). Fix: reject
every '&'-anchor property unconditionally instead of building a hand-rolled
duplicate registry, which guarantees zero fail-open by construction at the
cost of a deliberate, endorsed over-reject on a single non-duplicated
anchor. The '!' non-specific-tag branch is unchanged ('! x' -> 'x' still
resolves).
This commit is contained in:
@@ -1079,21 +1079,28 @@ assert_token "NEL in a comment fails closed" "" primary git.example
|
||||
write_break_fixture 0x85 dq2
|
||||
assert_fallback_fails_closed "blank-line-in-quote (NEL run) fails closed" primary git.example
|
||||
|
||||
# 24. LEADING NON-SPECIFIC TAG / ANCHOR PROPERTY (#865 round-11 blocker B): the
|
||||
# round-10 recognizer blanket-rejected any scalar beginning with '!' or '&'.
|
||||
# But a NON-SPECIFIC tag '! ' (bang + SPACE) and an anchor '&name ' are
|
||||
# transparent node properties: PyYAML strips them and applies normal implicit
|
||||
# typing to the node, so '! x'/'&a x' resolve the STRING 'x'. The fallback now
|
||||
# consumes <=1 non-specific tag and <=1 anchor (either order) and recurses on
|
||||
# the remainder. Verified empirically vs real PyYAML 6.0.3.
|
||||
for pair in '! x=x' '&a x=x' '! x y=x y' '! "q"=q' "! 'q'=q" '! &b x=x' '&b ! x=x'; do
|
||||
# 24. LEADING NON-SPECIFIC TAG / ANCHOR PROPERTY (#865 round-11 blocker B, revised
|
||||
# in round 12): the round-10 recognizer blanket-rejected any scalar beginning
|
||||
# with '!' or '&'. Round 11 taught it to strip a transparent NON-SPECIFIC tag
|
||||
# ('! ' bang + SPACE) and a transparent plain ANCHOR ('&name ') so '! x' /
|
||||
# '&a x' resolve the STRING 'x', matching PyYAML. Round 12 discovered that the
|
||||
# anchor half of that was a HIGH fail-open: PyYAML's Composer tracks anchor
|
||||
# NAMES in a document-scoped registry and raises ComposerError ("found
|
||||
# duplicate anchor") the instant the SAME name is declared on a SECOND node
|
||||
# ANYWHERE in the document (even an unrelated one) -- the fallback's
|
||||
# per-scalar-only view has no such registry and would emit the later token.
|
||||
# Round 12's fix: reject EVERY '&'-anchor property, unconditionally. The
|
||||
# transparent NON-SPECIFIC TAG behavior ('! x' -> 'x') is unchanged and still
|
||||
# verified below; only the anchor half now fails closed (deliberate
|
||||
# conservative over-reject, verified safe both ways against real PyYAML 6.0.3).
|
||||
for pair in '! x=x' '! x y=x y' '! "q"=q' "! 'q'=q"; do
|
||||
tv="${pair%%=*}"; want="${pair#*=}"
|
||||
write_fixture "logins:
|
||||
- name: primary
|
||||
url: https://git.example
|
||||
token: ${tv}
|
||||
"
|
||||
assert_token "property token '$tv' resolves node string" "$want" primary git.example
|
||||
assert_token "tag property token '$tv' resolves node string" "$want" primary git.example
|
||||
done
|
||||
|
||||
# Fail-OPEN direction stays shut. '!x' (bang + NON-space) is a tag HANDLE ->
|
||||
@@ -1109,6 +1116,35 @@ for tv in '!x' '!foo x' '* a' '! !x' '! 123' '! true' '! null' '&a &b x'; do
|
||||
assert_token "non-resolving property token '$tv' fails closed" "" primary git.example
|
||||
done
|
||||
|
||||
# Round 12: a SINGLE, non-duplicated '&a x' is valid YAML that real PyYAML
|
||||
# resolves to the string 'x' (round-11 behavior, and still true of the oracle).
|
||||
# The fallback now rejects it anyway -- a deliberate, endorsed CONSERVATIVE
|
||||
# over-reject (see the round-12 comment block above): fail-closed can only cost
|
||||
# an emitted token PyYAML would have allowed, never emit one PyYAML rejects, and
|
||||
# a per-scalar recognizer cannot safely prove document-wide anchor-name
|
||||
# uniqueness. assert_fallback_fails_closed also confirms PyYAML really does
|
||||
# resolve a token here, proving this is a genuine (safe-direction) divergence
|
||||
# and not an accidental parity loss.
|
||||
write_fixture 'logins:
|
||||
- name: primary
|
||||
url: https://git.example
|
||||
token: &a x
|
||||
'
|
||||
assert_fallback_fails_closed "round-12: single non-duplicated anchor '&a x' now fails closed (conservative over-reject; PyYAML resolves x)" primary git.example
|
||||
# Same over-reject for the combined tag+anchor forms round 11 used to resolve.
|
||||
write_fixture 'logins:
|
||||
- name: primary
|
||||
url: https://git.example
|
||||
token: ! &b x
|
||||
'
|
||||
assert_fallback_fails_closed "round-12: '! &b x' (tag+anchor) now fails closed (conservative over-reject)" primary git.example
|
||||
write_fixture 'logins:
|
||||
- name: primary
|
||||
url: https://git.example
|
||||
token: &b ! x
|
||||
'
|
||||
assert_fallback_fails_closed "round-12: '&b ! x' (anchor+tag) now fails closed (conservative over-reject)" primary git.example
|
||||
|
||||
# Endorsed fail-closed over-reject: an EXPLICIT tag ('!!str x', verbose
|
||||
# '!<tag:yaml.org,2002:str> x') forces a string PyYAML resolves, but the fallback
|
||||
# recognizes only the transparent non-specific tag and fails closed (safer).
|
||||
@@ -1121,4 +1157,56 @@ for tv in '!!str x' '!<tag:yaml.org,2002:str> x'; do
|
||||
assert_fallback_fails_closed "explicit-tag token '$tv' fails closed" primary git.example
|
||||
done
|
||||
|
||||
# 25. #865 round 12 HIGH fail-open closure: DUPLICATE ANCHOR NAME across separate
|
||||
# nodes. Real PyYAML's Composer tracks anchor names in a DOCUMENT-SCOPED
|
||||
# registry and raises ComposerError ("found duplicate anchor ... first
|
||||
# occurrence") the instant the SAME anchor name is declared a second time
|
||||
# ANYWHERE in the document -- failing the WHOLE document closed, no token,
|
||||
# regardless of how far the duplicate sits from the logins block. The round-11
|
||||
# fallback tracked anchors only WITHIN a single scalar's `_strip_properties`
|
||||
# call, so it had no visibility into a duplicate declared on an unrelated
|
||||
# node and would still emit the (later) token: fail-open. The round-12 fix
|
||||
# (reject every '&'-anchor property, unconditionally -- see section 24 above)
|
||||
# closes this as a strict superset: since NO anchor is ever accepted, a
|
||||
# duplicate anchor can never slip through. These cases exercise that
|
||||
# document-wide duplicate-anchor invariant specifically (as opposed to
|
||||
# section 24's single-anchor-on-the-token-field cases) and pair each fallback
|
||||
# assertion with confirmation that real PyYAML also fails closed here (via
|
||||
# ComposerError), proving this was a genuine fail-open, not a hypothetical.
|
||||
write_fixture 'first: &same one
|
||||
second: &same two
|
||||
logins:
|
||||
- name: primary
|
||||
url: https://git.example
|
||||
token: TOK_DUP_ROOT
|
||||
'
|
||||
assert_both_fail_closed "round-12: duplicate anchor name on two unrelated root nodes fails closed" primary git.example
|
||||
|
||||
write_fixture 'outer:
|
||||
nested: &dup x
|
||||
dup_root: &dup y
|
||||
logins:
|
||||
- name: primary
|
||||
url: https://git.example
|
||||
token: TOK_DUP_NESTED
|
||||
'
|
||||
assert_both_fail_closed "round-12: duplicate anchor name across a nested node and a root node fails closed" primary git.example
|
||||
|
||||
write_fixture 'first: &dup one
|
||||
logins:
|
||||
- name: primary
|
||||
url: https://git.example
|
||||
token: &dup TOK_DUP_TOKEN_NODE
|
||||
'
|
||||
assert_both_fail_closed "round-12: anchor name declared earlier and repeated on the token-bearing node fails closed" primary git.example
|
||||
|
||||
# Regression guard: the non-specific TAG half of section 24 ('! x' -> 'x') is
|
||||
# UNCHANGED by the round-12 anchor fix and must still resolve.
|
||||
write_fixture 'logins:
|
||||
- name: primary
|
||||
url: https://git.example
|
||||
token: ! x
|
||||
'
|
||||
assert_token "round-12 regression: '! x' (tag, no anchor) still resolves 'x'" "x" primary git.example
|
||||
|
||||
echo "Gitea login resolution regression harness passed"
|
||||
|
||||
Reference in New Issue
Block a user