Reject unversioned party documents

This commit is contained in:
2026-09-02 11:57:51 +00:00
parent 2cfca710ec
commit 61095e69ce
29 changed files with 172 additions and 293 deletions

View File

@@ -83,17 +83,12 @@ type PartyClass struct {
Level *int
}
// ParseParty classifies and, when selected by schema_version, strictly parses
// a party source. An unversioned source takes the deliberately opaque legacy
// compatibility path; see party_legacy.go.
// ParseParty strictly parses one supported party document.
func ParseParty(data []byte) (*PartyDocument, error) {
root, decoder, err := parsePartyFirstDocument(data)
if err != nil {
return nil, err
}
if !partyDocumentSelectsCanonical(root) {
return classifyLegacyPartyDocument(), nil
}
if err := requireNoTrailingPartyDocuments(decoder); err != nil {
return nil, err
}
@@ -119,25 +114,12 @@ func parsePartyFirstDocument(data []byte) (*yaml.Node, *yaml.Decoder, error) {
return document.Content[0], decoder, nil
}
func partyDocumentSelectsCanonical(root *yaml.Node) bool {
if root == nil || root.Kind != yaml.MappingNode {
return false
}
for index := 0; index+1 < len(root.Content); index += 2 {
key := root.Content[index]
if key.Kind == yaml.ScalarNode && key.Tag == "!!str" && key.Value == "schema_version" {
return true
}
}
return false
}
func requireNoTrailingPartyDocuments(decoder *yaml.Decoder) error {
var trailing yaml.Node
if err := decoder.Decode(&trailing); err == nil {
return fmt.Errorf("canonical party must contain exactly one YAML document")
return fmt.Errorf("party must contain exactly one YAML document")
} else if err != io.EOF {
return fmt.Errorf("decode trailing canonical party YAML: %w", err)
return fmt.Errorf("decode trailing party YAML: %w", err)
}
return nil
}
@@ -147,17 +129,21 @@ func parseCanonicalParty(root *yaml.Node, raw []byte) (*CanonicalParty, error) {
if err != nil {
return nil, err
}
version, ok := fields["schema_version"]
if !ok {
return nil, fmt.Errorf("party.schema_version is required; supported schema_version is %q", PartySchemaVersion)
}
schemaVersion, err := partyRequiredString(map[string]*yaml.Node{"schema_version": version}, "schema_version", "party")
if err != nil {
return nil, fmt.Errorf("party.schema_version must be a non-empty string; supported schema_version is %q", PartySchemaVersion)
}
if schemaVersion != PartySchemaVersion {
return nil, fmt.Errorf("party.schema_version %q is unsupported; supported schema_version is %q", schemaVersion, PartySchemaVersion)
}
if err := partyKnownFields(fields, "party", "schema_version", "characters"); err != nil {
return nil, err
}
version, err := partyRequiredString(fields, "schema_version", "party")
if err != nil {
return nil, err
}
if version != PartySchemaVersion {
return nil, fmt.Errorf("party.schema_version %q is unsupported", version)
}
charactersNode, ok := fields["characters"]
if !ok {
return nil, fmt.Errorf("party.characters is required")