Skip to content

status --where: unknown field name silently matches-all (no field validation) #314

@clkao

Description

@clkao

Summary

spacedock status --where does not validate that the queried field exists. An unknown or
misspelled field name is silently read as the empty string in applyFilters, so the clause matches
the wrong set with no error or warning — most dangerously, a != clause on an unknown field
matches every entity (silent match-all). A user mistake becomes a confidently-wrong result.

Repro (spacedock 0.19.0, contract 1)

# unknown field + "!=" → returns ALL entities, no error/warning
spacedock status --workflow-dir <wd> --where "nosuchfield!=x"

# the common way users hit it: TWO conditions in ONE --where string
# (the intuitive-but-wrong syntax) → silently returns ALL
spacedock status --workflow-dir <wd> --where "sprint=A sprint-readiness!=defer"

# unknown field + "=" → returns NONE, no error
spacedock status --workflow-dir <wd> --where "nosuchfield=x"

The correct form (separate flags) ANDs correctly and works:

spacedock status --workflow-dir <wd> --where "sprint=A" --where "sprint-readiness!=defer"

Root cause (internal/status/parse.go)

  • parseWhereFilters validates that an operator is present (whereSyntaxHelp) but never that the
    field exists.
  • For a compound single string it Cuts on the first operator (strings.Cut(whereArg, "!="), ~L187),
    so "sprint=A sprint-readiness!=defer" parses to field="sprint=A sprint-readiness", op="!=",
    value="defer" — a garbage field name, no error.
  • applyFilters (~L215): fieldVal := e.fields[f.field] returns "" for an unknown field (map zero
    value). For != with a non-nil value, fieldVal == *f.value is "" == "defer" → false → match
    stays true → the clause is satisfied by every entity. (For =, "" != value → excludes
    everyone.)

So an unknown field degrades to "absent", and "absent != X" is vacuously true for all → silent
match-all.

Impact

  • Typos (--where "spint=foo") silently return the wrong set with no signal.
  • The intuitive compound-in-one-string syntax silently returns the unfiltered set — users
    reasonably expect either AND-semantics or an error.
  • Real downstream footgun: a two-condition "active-sprint" task-selection recipe in a sibling project
    fell back to rg over the state dir precisely because a compound --where "didn't filter" — without
    realizing the correct form is repeated --where flags. A loud error would have pointed straight at
    the fix.

Expected

Validate f.field against the entity's known fields (frontmatter keys + computed/projectable columns)
at parse or apply time, and error with a clear message (à la whereSyntaxHelp) on an unknown field,
e.g.:

--where: unknown field "sprint=A sprint-readiness" (known: id, slug, status, sprint, sprint-readiness, pr, mod-block, ...)

Turning the silent wrong-result into a loud, fixable error is the fix; the silent match-all is the
dangerous part.

Not a bug (for contrast)

Multiple --where flags already AND correctly — parseWhereFilters returns a slice and applyFilters
is conjunctive. Verified: --where "sprint=repeatable-pairing" --where "sprint-readiness!=defer" → 0
results, while each clause alone returns its own set. So this report is about field validation, not
a missing AND operator.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions