Commit Graph

377 Commits

Author SHA1 Message Date
0f90b3a4d3 fix(schema): generalize redefine self-reference to attributeGroups + bound the walk; invalid-schemas-accepted 75 to 74 (#161)
Extends the redefine self-reference rule (redefineSelfReferenceErrors, moved to
SchemaRedefineSelfReference.swift) to attribute groups: a group or attributeGroup
inside xs:redefine may reference itself at most once (src-redefine.6.1.1 /
7.2.1), and a group self-reference must occur exactly once. Catches a redefined
attributeGroup with two self-references (attgC044).

Fixes a latent false positive an adversarial review found in the group case: the
search now uses a BOUNDED walk (boundedSelfReferenceNodes) that stops at nested
element/complexType scopes, matching the established cycle-rule boundary. The
prior unbounded walk would miscount a recursive group reference inside a nested
element's content (a data-structure recursion, legal) as a redefinition
self-reference. attributeGroup content has no such nesting, so it is unaffected.

XSTS: invalid-schemas-accepted 75 to 74 (attgC044), valid-schemas-rejected held
at 0, no other bucket moved. The validation description broadened accordingly
(configuration pin updated).
2026-06-18 01:15:16 +02:00
6379e5b25a fix(schema): an identity-constraint xpath may not use an undeclared prefix; invalid-schemas-accepted 77 to 75 (#145)
A selector/field xpath in an identity constraint may name elements/attributes
with namespace-qualified name tests; every prefix used must be a declared
xmlns: prefix in scope (the implicit xml prefix excepted). identityXPathErrors,
after the existing syntax check, now resolves each name-test prefix against the
in-scope bindings (namespaceBindingsInScope, which captures the schema root and
every ancestor) and rejects an undeclared one.

An adversarial review confirmed no false positive: a prefix declared on the
schema root, any intermediate ancestor, or the selector/field element resolves
correctly; xml, the child::/attribute:: axis keywords (colon-free), prefix:*,
and inter-token whitespace are all handled.

XSTS: invalid-schemas-accepted 77 to 75 (idI010, idJ011), valid-schemas-rejected
held at 0, no other bucket moved. (Two existing xpath-syntax fixtures used an
undeclared 'ns' prefix to test name-test syntax; the test schema now declares
xmlns:ns so they exercise syntax with a bound prefix.)
2026-06-18 01:01:12 +02:00
880137a955 fix(schema): a redefined group's self-reference must occur exactly once (src-redefine.6.1.2); invalid-schemas-accepted 79 to 77 (#161)
A group inside xs:redefine may reference itself (the group being redefined) at
most once, and that self-reference must have minOccurs = maxOccurs = 1. A
self-reference with minOccurs="0", maxOccurs="unbounded", or a count above 1 is
invalid. redefineGroupSelfReferenceErrors flags it, surfaced by the pre-compile
Validation redefineGroupSelfReferencesAreUnit.

The self-reference is matched by NAMESPACE (referenceNamespace == the redefining
schema's targetNamespace), not local name alone, so a redefined group that
references an imported group merely sharing its local name is correctly left
unconstrained -- the false-positive boundary an adversarial review flagged
before merge, mirroring the namespace guard the sibling redefine/cycle rules use.

XSTS: invalid-schemas-accepted 79 to 77 (schR3, schR4), valid-schemas-rejected
held at 0, no other bucket moved.
2026-06-18 00:52:40 +02:00
c45e1eb856 fix(schema): a local element must have a name or a ref; invalid-schemas-accepted 80 to 79 (#145)
A local element declaration (one not a child of schema) must identify itself
with a name or a ref; an xs:element with neither is invalid. localElementErrors
now flags it, alongside the existing local-element forbidden-attribute checks.

XSTS: invalid-schemas-accepted 80 to 79 (elemP005), valid-schemas-rejected held
at 0, no other bucket moved.
2026-06-18 00:34:50 +02:00
75dd044d93 fix(schema): a local complexType must be anonymous; invalid-schemas-accepted 81 to 80 (#145)
A complexType nested in an element is a local (anonymous) type definition and
may not carry a name attribute; only a top-level complexType (a child of schema,
or a redefine redefinition) is named. localComplexTypeNameErrors flags a named
complexType whose parent is an element, wired into the structure-walk validation.

XSTS: invalid-schemas-accepted 81 to 80 (ctA042), valid-schemas-rejected held
at 0, no other bucket moved.
2026-06-18 00:17:35 +02:00
059763f5e9 refactor(schema): surface facet/pattern validity as a Validation, not a parse-time report (#161)
The convergence audit for "validators everywhere" found the one remaining XSD
schema-validity verdict reported outside the Validator framework: facet
definition validity and pattern syntax validity were reported straight to the
diagnostics collector during simple-type compilation (validation folded into
parsing, which validation-rules.md forbids).

These checks genuinely need the resolved base type, computed during compilation,
so the finding is gathered into a context finding sink (XSDContext
.facetFindingSink) during type resolution and surfaced by the named post-compile
Validation simpleTypeFacetsAreValid -- located findings preserved, now pinned in
the configuration test and removable by identity like every other rule.

Re-sweep confirms the XSD path has converged: the only remaining throws are
notASchema (a not-a-schema precondition) and inconsistent (the framework's own
surfacing throw); no validity verdict bypasses the framework. No bucket change
(invalid-schemas-accepted 81, valid-schemas-rejected 0); full suite + fuzz pass.
2026-06-18 00:02:20 +02:00
4def6a2c07 refactor(schema): redefine self-derivation as a Validation; drop the redundant all-group throw (#161)
Continues incorporating every schema-validity check into the Validator framework
(Rules/universal/validation-rules.md) rather than ad-hoc parse-pipeline throws.

- checkRedefine (src-redefine.5: a redefined type restricts or extends itself)
  becomes the pre-compile Validation redefinitionsDeriveFromThemselves;
  redefineDerivationErrors returns findings instead of throwing.

- checkAllGroups is removed entirely: every check it made is already produced by
  Validations -- "a member must be an element" by schemaStructureValid
  (allowedChildren[all]), and the occurrence limits by allGroupLimitedErrors
  (cos-all-limited.2). Verified redundant and not load-bearing: an empirical
  probe with the calls removed left invalid-schemas-accepted at 81 unchanged,
  the fuzz suite's "compile and validation never crash or hang" still passes
  (the NFA builder's own guards hold), and false positives stayed 0.

compileRule is now internal so composition Validations can live in their own
file (SchemaCompositionValidations.swift). Both new pre-compile rules are pinned
in ValidationConfigurationTests. The pre-existing public SchemaError.invalidAllGroup
case is kept (released API) though no longer thrown. No bucket change
(invalid-schemas-accepted 81, valid-schemas-rejected 0); full suite + fuzz pass.
2026-06-17 23:49:30 +02:00
d3a0b60d67 refactor(schema): express the src-resolve check as a Validation, not a pipeline throw (#161)
Per the OpenAPIKit validation idiom (Rules/universal/validation-rules.md), every
schema-validity check should run through the Validator framework as a described,
composable Validation rather than an ad-hoc throw. The src-resolve check (a
resolved include/import/redefine schemaLocation must be a well-formed schema)
was a parse-pipeline throw; it is now the pre-compile Validation
referencedSchemasResolveToSchemas.

failedSchemaReferences (formerly the throwing checkSchemaLocations) returns the
resolved-but-not-a-schema locations; the pipeline records them on XSDContext and
the validation reports one finding each. Same rejection behaviour (the finding
makes the schema invalid), now located in the framework, pinned in the
configuration test. Removed the now-unused SchemaError.invalidSchemaReference
case (added earlier this branch, never released).

No bucket change (invalid-schemas-accepted stays 81, valid-schemas-rejected 0);
full suite and fuzz pass (report-and-continue does not crash downstream).
2026-06-17 23:34:33 +02:00
a444604a80 fix(schema): a redefined type must derive from itself, not a same-named foreign type (src-redefine.5); invalid-schemas-accepted 84 to 81 (#161)
A type inside xs:redefine must restrict or extend the type it redefines, which
lives in the redefining schema's own target namespace. checkRedefine compared
local names only (derivationInfo strips the prefix), so a base like foo:b-ct
-- an imported type sharing the redefined type's local name -- was treated as
self and accepted. Now the base's namespace is resolved: a base bound to an
explicit namespace other than the redefining schema's target namespace is not
self and is rejected (src-redefine.5).

redefineBaseIsForeign (SchemaImportNamespace.swift) leaves a base with no
resolvable namespace alone, so an unprefixed or own-namespace self-reference
never over-rejects -- the false-positive boundary, the same namespace-aware
fix applied across the campaign.

XSTS: invalid-schemas-accepted 84 to 81 (schQ2/schQ4/schP3),
valid-schemas-rejected held at 0, no other bucket moved. Differential:
agree-valid unchanged at 3161, PureXML-stricter unchanged at 87 over 9618
schemas. Third step of the cross-document composition campaign (#161).
2026-06-17 23:00:07 +02:00
c418147d30 fix(schema): an import namespace must match the imported targetNamespace (src-import.3); invalid-schemas-accepted 87 to 84 (#161)
src-import.3.1/3.2: an xs:import's namespace attribute must equal the
targetNamespace of the schema document it resolves to (both present and equal,
or both absent). Importing a no-namespace document under a declared namespace,
or one whose target namespace differs, is now rejected.

importNamespaceErrors (new SchemaImportNamespace.swift) builds a resolved
schemaLocation -> targetNamespace map from the loaded containers and compares
each import node's namespace to it. Checked only when the schemaLocation
resolved to a loaded schema (an unresolved import is the processor's choice,
not an error) -- the false-positive boundary; imports keep their own target
namespace so no chameleon adjustment applies.

XSTS: invalid-schemas-accepted 87 to 84 (schF6/schG10/schG13),
valid-schemas-rejected held at 0, no other bucket moved. Differential:
agree-valid unchanged at 3161, PureXML-stricter unchanged at 87 over 9618
schemas. Second step of the cross-document composition campaign (#161).
2026-06-17 22:47:24 +02:00
7f2943cf5c fix(schema): a resolved schemaLocation must be a valid schema (src-resolve); invalid-schemas-accepted 95 to 87 (#161)
When an xs:include / xs:import / xs:redefine schemaLocation IS resolved (the
schema loader returns content for it), that content must be a well-formed
schema document. Not-well-formed XML, or well-formed XML whose root is not
xs:schema, now makes the composing schema invalid (SchemaError
.invalidSchemaReference). collectContainers previously loaded externals with
`try? parseTree` and silently dropped a malformed one, accepting the schema.

A location the loader does NOT resolve (returns nil) is not an error, since
resolution is the processor's choice (src-resolve) -- so a self-contained
schema, or one whose externals are simply unavailable, is unaffected. This is
the false-positive boundary: fire only when the loader returned content that
is not a schema.

XSTS: invalid-schemas-accepted 95 to 87 (eight caught), valid-schemas-rejected
held at 0, no other bucket moved. Differential: agree-valid unchanged at 3161,
PureXML-stricter unchanged at 87 over 9618 schemas (zero new over-rejection).
First step of the cross-document composition campaign (#161).
2026-06-17 22:34:21 +02:00
615f668fc2 fix(schema): an attribute's type must be a simple type (au-props-correct); invalid-schemas-accepted 96 to 95 (#145)
The {type definition} of an attribute is a simple type (au-props-correct /
src-attribute). An attribute whose type resolves to a complex type, or to the
complex ur-type xs:anyType, is now rejected; a built-in or user simple type,
an inline simpleType, or no type (anySimpleType) stays valid. A locally-
declared type takes precedence over the built-in reading, consistent with the
src-ct rules.

Strictly zero false positive: an attribute can never carry a complex type, so
no valid schema is affected. XSTS: invalid-schemas-accepted 96 to 95 (attD002),
valid-schemas-rejected held at 0, no other bucket moved. Differential:
PureXML-stricter unchanged at 87 over 9618 schemas.
2026-06-17 22:12:01 +02:00
a2047956cb fix(schema): enforce element value-constraint content type (cos-valid-default); invalid-schemas-accepted 99 to 96 (#145)
An element with a default or fixed value constraint must have a content type
that admits character data: a simple type, or mixed content (cos-valid-default
/ e-props-correct). An element whose type is, or whose inline complex type has,
element-only or empty content may not carry a value constraint.

elementValueConstraintContentErrors flags only a content type that is certainly
element-only or empty; a simple type, mixed content, xs:anyType (mixed), an
untyped element (anyType), a complexContent-derived type (mixedness may be
inherited), and a foreign or unresolved type are all left alone, so it cannot
over-reject.

XSTS: invalid-schemas-accepted 99 to 96 (elemG003/elemG004/elemD004),
valid-schemas-rejected held at 0, no other bucket moved. Differential:
PureXML-stricter unchanged at 87 over 9618 schemas.
2026-06-17 22:00:23 +02:00
9c57dedb64 fix(schema): enforce derivation base-type kind (src-ct.1/.2); invalid-schemas-accepted 104 to 99 (#145)
A content-model derivation's base must match its content-model keyword
(XSD 1.0 Structures 3.4.6):

- src-ct.1: a complexContent restriction/extension base must be a complex
  type. A simpleType or a built-in simple type (xs:string) is rejected; the
  ur-type xs:anyType is complex and remains a valid base.

- src-ct.2 (extension): a simpleContent extension base must be a simple type
  or a complex type with simple content. A complex type with element-only,
  mixed, or empty content, including xs:anyType, is rejected.

A locally-declared type takes precedence over the built-in reading, so a
schema that targets the XSD namespace and extends its own complex types
(the schema-for-schemas, e.g. xs:openAttrs) is not rejected. This caught a
self-inflicted false positive during development; the fix restores it.

XSTS: invalid-schemas-accepted 104 to 99 (ctE003/ctE004/ctK002/ctJ002/
ctJ003), valid-schemas-rejected held at 0, no other bucket moved. The
differential corroborates: PureXML-stricter unchanged at 87 over 9618
schemas (zero new over-rejection).
2026-06-17 21:49:12 +02:00
606bc35a45 fix(schema): enforce identity-constraint content model; invalid-schemas-accepted 110 to 104 (#145)
An xs:unique / xs:key / xs:keyref content model is (annotation?, selector,
field+): exactly one selector, then one or more fields, in that order. The
structure walk previously checked only that the children were drawn from
{annotation, selector, field}, not their cardinality or order, so a field
before the selector, a second selector, or a missing field was accepted.

identityConstraintContentErrors enforces it, wired into the per-component
structural dispatch. XSTS: invalid-schemas-accepted 110 to 104 (six caught:
idA044/046, idB044/046, idC047/049), valid-schemas-rejected held at 0, no
other bucket moved.
2026-06-17 21:04:16 +02:00
b391db6a74 fix(schema): enforce model-group occurrence ranges; invalid-schemas-accepted 130 to 110 (#145)
Enforce the two occurrence-range constraints on model groups that PureXML
was not checking:

- p-props-correct.1: a particle's minOccurs may not exceed its maxOccurs,
  both defaulting to 1 when the attribute is absent. occurrenceOrderErrors
  previously fired only when BOTH attributes were present, so it missed
  minOccurs="2" with a defaulted maxOccurs (2 > 1) and maxOccurs="0" with a
  defaulted minOccurs (1 > 0). It now orders against the effective defaults.

- cos-all-limited.2: an all group's maxOccurs must be 1 and its minOccurs
  0 or 1, and every particle it contains is limited to maxOccurs 0 or 1.

Both are zero-false-positive rules (no valid schema violates them). XSTS:
invalid-schemas-accepted 130 to 110 (20 caught), valid-schemas-rejected
held at 0, no other bucket moved. The differential harness corroborates:
agree-invalid 1751 to 1767, PureXML-stricter unchanged at 87 (no new
over-rejection across 9618 schemas).
2026-06-17 20:55:22 +02:00
d92b9149df fix(schema): eliminate the last false positive (particlesZ001); false-positives = 0 (#148)
Read element-vs-choice Particle Valid (Restriction) through effective total
range: an element validly restricts a base choice when its leaf is a
NameAndTypeOK restriction of an element the choice admits and its effective
occurrence range is within the choice's. This fixes particlesZ001 (the
RecurseAsIfGroup rule its own test calls ambiguous; W3C reads it valid) while
keeping particlesM002/003 valid and particlesL rejected.

Named, bounded under-rejection (exact in-branch sequencing unmodelled), the
production-sanctioned direction: invalid-schemas-accepted 128->130,
invalid-instances-accepted 132->133 (particlesZ001.i, a masked gap now exposed).

XSTS valid-schemas-rejected 1->0. With valid-instances-rejected already 0,
false positives are now 0 in both buckets (production stopper #1).
2026-06-17 19:56:57 +02:00
ceca006c45 fix(schema): a restriction's attribute wildcard must subset the base's (#145)
cos-ct-restricts.4: a base type with no attribute wildcard admits none (so a
restriction may not introduce one), and a wider namespace constraint such as
##any over ##other, or a weakened processContents, is not a subset. Reuses the
wildcard subset check (ParticleRestriction.narrows, now internal).

XSTS invalid-schemas-accepted 131 to 128, no other bucket moved.
2026-06-17 18:46:04 +02:00
c029505c3e fix(schema): a restriction may not add an attribute outside the base wildcard (#145)
An attribute a complexContent restriction declares with no matching base
attribute use must be admitted by the base type's anyAttribute wildcard
(cos-ct-restricts.3); e.g. a no-namespace attribute is not admitted by a
##other wildcard. A prohibited declaration still validly removes an attribute.

XSTS invalid-schemas-accepted 132 to 131, no other bucket moved.
2026-06-17 18:31:40 +02:00
f61e7d0613 fix(schema): reject an empty-string namespace value (#145)
A namespace name must be a non-empty anyURI, so <schema targetNamespace=""> and
<import namespace=""> are invalid. Omitting the attribute (no target namespace,
or a no-namespace import) stays valid.

XSTS invalid-schemas-accepted 134 to 132, no other bucket moved.
2026-06-17 18:07:11 +02:00
30421073a1 fix(schema): a wildcard restriction may not weaken processContents (#145)
A restricting xs:any must have processContents at least as strong as the base
wildcard's (strict > lax > skip; Wildcard Subset, XSD 1.0 sec 3.10.6), in
addition to narrowing the namespace constraint.

XSTS invalid-schemas-accepted 137 to 134, no other bucket moved.
2026-06-17 17:56:47 +02:00
903345e764 fix(schema): a simpleContent restriction's base must be a complex type (#145)
Restricting a built-in or user simple type through <simpleContent><restriction>
is invalid (cos-ct-restricts.1 / src-ct.2): a complex type with simple content
is built by extending a simple type, not by restricting one. A unit-test
fixture that restricted xs:string directly was spec-invalid and now restricts a
complexType with simple content instead.

XSTS invalid-schemas-accepted 142 to 137, no other bucket moved.
2026-06-17 17:37:55 +02:00
bdd9e96634 fix(schema): enforce NameAndTypeOK nillable clause in particle restriction (#145)
A restricting element may not be nillable unless the base element it restricts
is also nillable. The element particle (Term.element) now carries a nillable
flag, populated from the element's nillable attribute.

XSTS invalid-schemas-accepted 144 to 142, no other bucket moved.
2026-06-17 17:25:10 +02:00
ef28eac926 fix(schema): enforce NameAndTypeOK fixed-value clause in particle restriction (#145)
When the base element has a fixed value constraint, a restricting element must
also be fixed to the same value, compared in the base type's value space so a
value-equal pair written in different lexical forms is not wrongly flagged.

XSTS invalid-schemas-accepted 148 to 144, no other bucket moved.
2026-06-17 17:08:55 +02:00
d43d1bb1bb fix(schema): subset-check anonymous complex-type restrictions (#145)
Particle Valid (Restriction) was keyed by type name, so an inline complexType
deriving by complexContent restriction was never checked against its base. Such
anonymous restrictions are now compiled and run through the same subset
algorithm as named types.

XSTS invalid-schemas-accepted 163 to 148, no other bucket moved.
2026-06-17 17:02:20 +02:00
ee5d180ba3 fix(schema): enforce the NameAndTypeOK block-superset rule (#145)
Particle Valid (Restriction) now requires a restricting element's {disallowed
substitutions} to be a superset of the base element's: a restriction may block
more substitutions, never fewer. The element particle (Term.element) carries a
block set, populated from the element's block attribute or the schema's
blockDefault.

XSTS invalid-schemas-accepted 179 to 163, no other bucket moved.
2026-06-17 16:45:35 +02:00
c3876a261f fix(schema): check RecurseAsIfGroup occurrence in particle restriction (#145)
An element restricting a base model group is treated as a synthetic group with
minOccurs=maxOccurs=1 holding the element (XSD 1.0 sec 3.9.6), so its {1,1}
occurrence must be within the base group's range: a base group that must occur
two or more times cannot be restricted to a single element. The element's own
occurrence is still matched against the base branch it maps to, so an optional
element restricting a once-or-more base group stays valid.

XSTS invalid-schemas-accepted 181 to 179, no other bucket moved.
2026-06-17 16:34:09 +02:00
97817ab611 fix(schema): reject a redefine that redefines a component twice (#145)
A redefine may redefine a given component at most once (src-redefine.7.2.2);
two redefinitions of the same kind and name in one redefine are invalid.

XSTS invalid-schemas-accepted 182 to 181, no other bucket moved.
2026-06-17 16:17:00 +02:00
4551508dd9 fix(schema): a redefine must name components that exist in the redefined schema (#145)
Once the redefined document is loaded through a schemaLoader, every component a
redefine names (complexType, simpleType, group, attributeGroup) must have a
same-kind component of the same name in the redefined schema (src-redefine.6 /
7.2.1). A redefinition of a component that does not exist there is rejected; a
stand-alone schema (target not loaded) is unaffected.

XSTS invalid-schemas-accepted 183 to 182, no other bucket moved.
2026-06-17 16:10:57 +02:00
379a9ccd8c fix(schema): validate value constraints against inline types (#145)
An element or attribute default/fixed value is now validated against an inline
type (an inline simpleType, or an inline complex type with simpleContent) the
same way it already was against a named type reference. Validated against the
simpleContent base's value space, so only a clear value-space mismatch (a
non-numeric default on a decimal-based type) is flagged.

XSTS invalid-schemas-accepted 185 to 183, no other bucket moved.
2026-06-17 15:45:06 +02:00
4460508924 fix(schema): inherit finalDefault as a complex type's final (#145)
A complex type without its own final attribute now inherits the schema's
finalDefault as its {final}, so finalDefault=#all or restriction/extension
blocks a complexContent restriction or extension derived from it. An explicit
final= overrides the default to empty. Complex types previously honoured only
an explicit final (elements already inherited finalDefault).

XSTS invalid-schemas-accepted 189 to 185, no other bucket moved.
2026-06-17 15:31:48 +02:00
96373cd6a3 fix(schema): forbid global-only properties on a local element (#145)
A local element declaration (an element particle, not a direct child of schema)
may not carry abstract, final, or substitutionGroup, which belong to the
top-level element form only. Their presence is invalid at any value, so
abstract=false on a local element is rejected as well as abstract=true.

XSTS invalid-schemas-accepted 194 to 189, no other bucket moved.
2026-06-17 15:18:33 +02:00
fd97faf291 fix(schema): require complexContent extension mixed to agree with its base (#145)
A complexContent extension that explicitly states mixed must match its base
type's mixedness (cos-ct-extends.1.4.3.2.2.1): a mixed extension extends only a
mixed base, an element-only extension only an element-only base. An extension
that omits mixed inherits the base's setting and stays valid, so only an
explicit conflict is rejected (ctZ012b, which inherits, is not flagged).

XSTS invalid-schemas-accepted 197 to 194, no other bucket moved.
2026-06-17 15:11:13 +02:00
b0479fd329 fix(schema): require a content derivation in simpleContent/complexContent (#145)
A complexContent or simpleContent's content model requires exactly one
restriction or extension. An empty <complexContent/> declares no derivation
and is now rejected as a structural error.

XSTS invalid-schemas-accepted 200 to 197, no other bucket moved.
2026-06-17 14:58:58 +02:00
947278bb56 fix(regex): reject a malformed \p{...} property name in a pattern facet (#145)
A \p{...}/\P{...} property name that can be no XSD charProp under any
repertoire (empty, the bare prefix Is, a non-block name carrying a non-letter,
or Is followed by a non-block character) is now a schema-validity error,
distinct from a well-formed but unrecognised category or block name, which
stays a lenient engine limitation.

XSTS invalid-schemas-accepted 203 to 200, no other bucket moved.
2026-06-17 14:53:32 +02:00
7e30526b2c fix(schema): reject attribute ref+form and an attribute named xmlns (#145)
An attribute ref is a use of a global declaration, so it may not also carry
form (src-attribute.3.2). And no attribute declaration may be named xmlns,
which is reserved for namespace declarations (no-xmlns).

XSTS invalid-schemas-accepted 207 to 203, no other bucket moved.
2026-06-17 14:39:31 +02:00
d402732d23 fix(schema): hold an attributeGroup reference to (annotation?) content (#145)
A referencing attributeGroup (one with a ref) names a definition elsewhere; its
own content model is (annotation?), so it may not also declare attributes,
nested attributeGroup references, or an anyAttribute. Only a defining
attributeGroup (with a name) carries those.

XSTS invalid-schemas-accepted 210 to 207, no other bucket moved.
2026-06-17 14:27:21 +02:00
709d23cc34 fix(schema): include notation names in component-name uniqueness (#145)
A notation declaration occupies its own symbol space, so two notations sharing
a name and target namespace are a duplicate just as two global types or
elements would be. Notations were omitted from the uniqueness check.

XSTS invalid-schemas-accepted 211 to 210, no other bucket moved.
2026-06-17 14:21:57 +02:00
616ec4876a fix(schema): hold xsd:notation to its (annotation?) content model (#145)
A notation declaration's content model is (annotation?): it may carry only an
optional annotation child. Any other element child, or any non-whitespace
character content, is now rejected.

XSTS invalid-schemas-accepted 214 to 211, no other bucket moved.
2026-06-17 14:17:27 +02:00
d2438a957a fix(schema): require a list or union to declare its member types (#145)
A list simple type must draw its item type from exactly one of an itemType
attribute or an inline simpleType child; neither, or both, is invalid. A union
must declare at least one member type through a memberTypes attribute or an
inline simpleType child; an empty union declares none (src-simple-type.2/.3).

XSTS invalid-schemas-accepted 215 to 214, no other bucket moved.
2026-06-17 14:11:26 +02:00
8420c7e2aa fix(schema): inherit finalDefault as a simple type's final (#145)
A simple type that declares no `final` of its own takes the schema's
`finalDefault` as its `{final}` property (XSD 1.0 Schemas section 3.14.2). The
list and union derivation checks read the `final` attribute directly but
ignored `finalDefault`, so `finalDefault="list"` or `"union"` did not block a
list item type or union member type and the schema was wrongly accepted.

The final map now falls back to `finalDefault` for a type without its own
`final`; an explicit `final=""` still overrides the default back to no
restriction, and a built-in item or member type stays unaffected (it is matched
only when the reference resolves to the schema's own namespace).

Caught two invalid schemas: XSTS invalid-schemas-accepted 217 -> 215, no other
bucket moved.
2026-06-17 13:59:19 +02:00
50c6fb9b1c fix(regex): reject a pattern facet whose regex ends mid-token (#145)
A `pattern` facet's value must be a valid XSD regular expression. Two
truncation cases were compiled with a non-rejecting error and the schema was
wrongly accepted:

- a trailing `\` with no character to escape (`a\`), and
- a `[` character class the expression ends before closing with `]`
  (`a[`, `[a[:xyz:`).

Both are unambiguously invalid, never an engine limitation on an otherwise
valid pattern, so they now raise the dedicated `RegexError.incompleteEscape`
and `RegexError.unterminatedClass` and `rejectsPattern` treats them as
schema-validity errors. A completed escape (`\[`, `\\`), a closed class, and a
class containing an escaped `]` or a subtraction stay valid.

Caught thirteen invalid schemas: XSTS invalid-schemas-accepted 230 -> 217, no
other bucket moved.
2026-06-17 13:53:41 +02:00
c80a8e06c4 fix(schema): reject an attribute reference whose fixed value contradicts its global (#145)
An attribute use that references a global attribute carrying a `fixed` value
constraint may not contradict it (au-props-correct.2): a `fixed` on the
reference whose value differs from the global's, or a `default` on a reference
to a fixed attribute, is invalid. A reference that omits its own value
constraint inherits the global's and stays valid.

The two `fixed` values are compared in the attribute type's value space, so a
value-equal pair written in different lexical forms (`01` for `1`) is not
flagged. The global is resolved by local name and only checked once its own
namespace matches the reference's resolved namespace, so a same-local-name
attribute imported from another namespace cannot surface as the wrong global
and contradict a valid reference.

Caught two invalid schemas: XSTS invalid-schemas-accepted 232 -> 230, no other
bucket moved.
2026-06-17 13:43:42 +02:00
a96e5ce3ac fix(schema): reject extending a non-empty all-group base with element content (#145)
Extending a complexType whose whole content is a non-empty xs:all group, via
complexContent extension that adds element content, nests that all in the
sequence joining base and extension content, which cos-all-limited forbids
(an all group must be the whole content model). Such schemas were accepted.

This is the symmetric companion to the existing rule (an all group in the
extension over a base with content); both now live in one pass. An empty-all
base contributes no content, and an attribute-only extension adds none, so
both stay valid.

XSTS invalid-schemas-accepted 233 to 232, no other bucket moved.
2026-06-17 13:23:26 +02:00
ac242a980b fix(schema): reject complexContent extension adding element content over a simpleContent base (#145)
A complexContent extension that adds element content (a non-empty model
group) produces an element-only/mixed content type, so its base must have
complex or empty content, not simpleContent (cos-ct-extends.1.4.2.2). Such
schemas were accepted.

The check requires the extension to add actual element content (an element or
wildcard, not an empty group), so an attribute-only complexContent extension
of a simpleContent base stays valid (the complexContent-around-simpleContent
idiom, e.g. particlesZ031).

XSTS invalid-schemas-accepted 235 to 233, no other bucket moved.
2026-06-17 13:02:37 +02:00
39649a1940 fix(schema): reject named-group compositor occurrence and XSD-namespace attributes (#145)
Two schema-for-schemas structural constraints were not enforced, so the
malformed schemas were accepted:

- The compositor of a top-level named xs:group (all/choice/sequence) may not
  carry minOccurs/maxOccurs; those belong on a group reference, not the
  definition.
- An attribute in the XML Schema namespace (e.g. xsd:type) is never valid on a
  schema element, since the vocabulary defines no namespaced attributes. A
  foreign attribute in any other namespace stays the author's own.

XSTS invalid-schemas-accepted 244 to 235, no other bucket moved. (A third
candidate, form on a top-level declaration, was already enforced by the
existing top-level attribute rule, so no new check was needed.)
2026-06-17 11:08:33 +02:00
b4c1285d8e fix(regex): reject malformed pattern facets as schema-invalid (#145)
A pattern facet with an unambiguously malformed regular expression was
compiled leniently and the schema accepted. Three such constructs are now
rejected: a reversed character-class range ([b-a]), an empty class ([]),
and a reversed bounded quantifier ({37,17}). The parser raises distinct
RegexError cases (reversedRange, emptyClass, reversedQuantifier) that the
schema-validity check treats as invalid.

Constructs the engine merely does not support (an untabulated \p{Is...}
block, the lenient {,m} form) stay non-rejecting, so a valid pattern is
never refused. XSTS invalid-schemas-accepted 262 to 244, no other bucket
moved; the 27 regex unit tests still pass.
2026-06-17 10:49:09 +02:00
fe8a6e8d6e fix(schema): resolve identity attribute-field types for value-space keys (#147)
An identity-constraint attribute field's type was resolved only among the
constraint host's descendant elements, so when the selector targets a global
element referenced from the host (e.g. unique over `.//uid` with field
`@val`), the field type was unknown and values compared lexically. Two keys
equal in value space but not lexically (3.0 and 3 for xsd:decimal) were then
treated as distinct and the duplicate was accepted.

The attribute type now resolves through the selector's target global element,
so values compare in their value space. XSTS invalid-instances-accepted
133 to 132 (idF010), no other bucket moved.
2026-06-17 10:34:19 +02:00
39a91c258e fix(schema): resolve identity-constraint XPath prefixes against the schema (#147)
Identity-constraint selector and field XPath prefixes were resolved against
the instance document's namespace bindings, so a constraint declared with
xmlns:v="..." and a selector like .//v:vehicle matched nothing when the
instance used a default namespace instead of the v prefix. The selector then
selected no nodes, no key tuples were collected, and duplicate-key and
keyref violations went undetected.

Prefixes now resolve against the namespace bindings in scope where the
constraint is declared in the schema (XSD Part 1, Identity-constraint
Definition), captured at parse time onto IdentityConstraint. A QName field
value still compares using the instance's bindings, so QName keys are
unaffected.

XSTS invalid-instances-accepted 143 to 133: the idc004/5/6 namespaced
key/keyref cluster and namespace-resolution cases (idZ010 and kin), no other
bucket moved.
2026-06-17 00:03:13 +02:00
9769f022dd fix(schema): enforce identity-constraint field cardinality (#147)
An identity-constraint field must evaluate to a node-set with at most one
member (cvc-identity-constraint.3). The validator built a value tuple per
target without checking how many nodes each field selected, so an instance
whose unique/key/keyref field matched several nodes was accepted.

fieldCardinalityError now rejects a target whose field selects more than one
node, in both the key/unique and keyref paths. XSTS invalid-instances-accepted
4 → fewer (147 to 143), no other bucket moved.
2026-06-16 23:41:30 +02:00