Skip to content

candiedyaml: honour !!str tag in resolveInterface to prevent YAML 1.1 bool coercion#39

Open
ivzhelev wants to merge 1 commit into
mandelsoft:masterfrom
ivzhelev:fix/candiedyaml-str-tag-coercion
Open

candiedyaml: honour !!str tag in resolveInterface to prevent YAML 1.1 bool coercion#39
ivzhelev wants to merge 1 commit into
mandelsoft:masterfrom
ivzhelev:fix/candiedyaml-str-tag-coercion

Conversation

@ivzhelev

Copy link
Copy Markdown

Problem

resolveInterface in legacy/candiedyaml/resolver.go ignores an explicit !!str tag when dispatching scalar values. A scalar such as !!str off has event.tag = "tag:yaml.org,2002:str" and event.implicit = false, but the existing guard at line 367:

if len(event.tag) == 0 && !event.implicit {
    return "", val
}

does not fire — because len(event.tag) != 0. The function then falls through to the character-based type dispatch, where 'o' matches the bools byte set and resolve_bool("off") succeeds, returning (yaml_BOOL_TAG, false).

The result: any YAML emitter that writes !!str off (the standard way to protect a YAML 1.1 boolean-colliding value) produces data that candiedyaml decodes as bool(false) instead of string("off"). Any downstream code relying on string semantics (jq paths like .config.basic_auth_handling // "default", string comparisons) then silently fails.

Fix

Add an early-return guard for the !!str tag immediately after the existing implicit/no-tag guard:

// Honour an explicit !!str tag: the YAML spec requires that an explicit
// tag overrides implicit type resolution.
if string(event.tag) == yaml_STR_TAG {
    return yaml_STR_TAG, val
}

This is a 3-line change. All other type dispatch (int, float, bool, null, binary, timestamp) is unchanged.

The resolve() function for typed decodes is unaffected — it dispatches on the target Go reflect.Kind before calling resolveInterface, so resolve_bool still handles !!bool off and decoding !!str off into a bool field still returns the expected error.

Tests

Adds a new Ginkgo context "Explicit !!str tag preserves string (no bool coercion)" in resolver_test.go covering all 16 YAML 1.1 boolean tokens (on, ON, off, OFF, yes, YES, no, NO, true, TRUE, false, FALSE, y, Y, n, N) tagged with !!str.

Fixes #38

… bool coercion

When `resolveInterface` is called for a scalar that carries an explicit
`!!str` tag (e.g. `!!str off`), it currently ignores the tag and falls
through to the character-based type dispatch, where `'o'` matches the
`bools` byte set and `resolve_bool("off")` returns `bool(false)`.

The YAML specification requires that an explicit tag overrides implicit
type resolution. Add an early-return guard:

    if string(event.tag) == yaml_STR_TAG {
        return yaml_STR_TAG, val
    }

This mirrors the existing guard for untagged non-implicit scalars
(line 367) and is consistent with how `resolve_string` already handles
typed decodes — it returns the raw string value whenever the target
Go type is `string`.

The `resolve()` function (typed decodes) is unaffected: it dispatches
on the target Go `reflect.Kind` before reaching `resolveInterface`, so
decoding a `!!str off` scalar into a `bool` field still produces the
correct error via `resolve_bool`.

Also adds a Ginkgo test context "Explicit !!str tag preserves string
(no bool coercion)" covering all 16 YAML 1.1 boolean tokens.

Fixes: mandelsoft#38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

candiedyaml: resolveInterface ignores !!str tag and coerces YAML 1.1 bool tokens to boolean

1 participant