Skip to content

spec §4.6 Imports "Rules:" omits symlink rejection — spec/implementation drift on a security control #327

Description

@dean0x

The normative Rules: list in spec.md §4.6 Imports does not mention that symlinked import paths are rejected, even though rejection is both documented elsewhere and enforced in code.

The gap

spec.md §4.6 Imports (spec.md:309) ends with a Rules: list at spec.md:338-345:

  • Relative paths only (no bare module names)
  • as alias namespaces all exports: access via {{alias.name}}
  • Without alias (merge): exports enter current scope (name collision → compilation error)
  • Selective: only listed names are brought into scope
  • Circular imports → compilation error
  • Import resolution is recursive (imports can import)

Nothing about symlinks.

What actually happens

Symlinked import paths are refused. This is:

  • DocumentedSECURITY.md:37-39: "Symlink rejection: symlinked import paths are refused. Resolution is written to be TOCTOU-safe (the resolved target is validated, not just the pre-resolution path)."
  • EnforcedNativeFs::check_symlink at crates/mds-core/src/fs.rs:343, reached from the import-resolution path and applied by every surface that reads a file.

So a reader working from the normative spec alone would conclude that @import "./link.mds", where link.mds is a symlink, resolves like any other relative import. It does not — it errors.

Why this matters

This is spec/implementation drift on a security control, which is the category where drift is least acceptable. spec.md is the normative document; a security guarantee that appears only in SECURITY.md is documented but not specified. Anyone implementing MDS from the spec — or writing conformance tests against it — would omit the control and produce something that looks conformant while being materially less safe.

The editorial call

The §4.6 Rules list is otherwise purely semantic — naming, scoping, collisions, cycles, recursion. Filesystem-level constraints are a different category, and there are two reasonable placements:

  1. Add symlink rejection (and possibly the sibling controls — .. traversal rejection, NUL-byte rejection, non-UTF-8 path rejection, all listed at SECURITY.md:33-43) to the §4.6 Rules list, so the import contract is complete in one place.
  2. Add a dedicated normative security/filesystem section to spec.md and cross-reference it from §4.6, keeping §4.6 semantic.

Option 2 scales better if the other fs.rs controls are specified too. Either way the current state — normatively silent — is the one option that should not persist.

Why deferred

Found during design work and deliberately scoped out of the current change, which is not touching spec.md. Filing so the drift is tracked rather than rediscovered.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentation

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions