scope-escape

Identifiers outside the declared scope are refused

shipped

The sections below are generated from the CLI (methodology 0.3-draft, CLI 0.3.0); the same text is printed by sealmark explain scope-escape.

pack
scope opt-in pack
weight
criticalcandidate
scope
server (does it refuse every identifier outside scope.allow, in every spelling?)
decided by
upstreamresponse
since
0.3-draft (methodology 0.3-draft)
config keys
checks.scope-escape.enabledchecks.scope-escape.severityscope.allowscope.casescope.denyscope.separatorscope.tools

Probes

  • control a call naming an exact scope.allow entry (up to three; else <prefix>sm_control_<tok> under the first prefix) must be processed and, with an upstream stub, reach it; otherwise the check is inconclusive
  • sibling <exact allow entry>_sm<tok>, a name next to an allowed one (sales.public.orders_sm<tok>)
  • container the bare containers of an allowed name (sales.public, then sales)
  • partial an allowed name cut short (sales.public.ord)
  • prefix-abuse a prefix compared without its separator (sales.publicx.t_sm<tok>); for an exact entry the entry plus x (sales.public.ordersx)
  • case the sibling in upper case (out of scope under either case rule)
  • quoted quoted segments ("sales"."public"."outside_sm<tok>")
  • separator-encoded the separator percent-encoded or replaced (sales%2Epublic%2Eoutside_sm<tok>, sales/public/outside_sm<tok>)
  • traversal dot-dot spellings (sales.public.orders.._sm<tok>, sales.public.orders/../outside_sm<tok>)
  • deny every scope.deny entry as written
  • case-control control, case: insensitive only: the upper-case spelling of an allowed name (SALES.PUBLIC.ORDERS) is expected to be accepted; a refusal is noted, never a fail

Settings and thresholds

Every check also takes checks.scope-escape.enabled and checks.scope-escape.severity; they are described once in the configuration reference.

This check has no numeric threshold.

KeyDefaultStandardMeaning
scope.allow[]not lockedIdentifiers the server may serve. An entry that ends with scope.separator is a prefix (sales.public.); any other entry is exact. Needed when scope.tools is not empty.
scope.caseinsensitivenot lockedWhether the server compares identifiers case-insensitively (the default) or case-sensitively. An upper-case spelling of an allowed name that is refused under insensitive is noted, never failed.
scope.deny[]not lockedIdentifiers the server must refuse even inside scope.allow (same syntax; deny wins).
scope.separator.not lockedThe one character that separates the parts of an identifier (and ends a prefix entry).
scope.tools[]not lockedTools that take an identifier the server is meant to restrict (a table, dataset, path), as { tool, param }: the scope pack sends out-of-scope forms of the identifier through each. The tool keeps its classification.

How the verdict is established

  • Scoring. critical and scored: pass = 1 point, part = 0.5, fail = 0. A fail caps the pack's grade at D, whatever the other checks scored.

What to fix

Normalise the identifier first (decode percent and separator forms once, fold case the way scope.case says, strip quotes), then compare whole segments with the allow list: an entry ending in the separator is a prefix, any other entry is exact. Refuse siblings, bare containers (sales, sales.public), partial names, prefix tricks (sales.publicx.t), case variants of an out-of-scope name, quoted and separator-encoded forms, traversal and every scope.deny entry, and refuse before anything is sent to the backend. If the control call was refused, check that scope.allow is spelled as the server lists its identifiers.

False positives

A server that serves an identifier from its own cache answers it without reaching the backend: that is a part (scope.served-locally), not a fail. A server declared case-insensitive that refuses the upper-case spelling is noted, never failed.

False negatives

Without an upstream stub the verdict rests on the reply alone. A scope rule that depends on something other than the identifier (the caller, the time) is not exercised. Only the forms in the corpus are sent; a name that hides in an encoding outside it passes.