fix(subagents): make acceptance checks portable on Windows (#5162)

* fix(subagents): make acceptance checks portable on Windows

* fix(subagents): reject drive-root path escapes

* fix(subagents): preserve drive-root containment

* fix(subagents): use Windows path casing rules

* fix(subagents): reject drive-relative cd paths

* fix(subagents): reject shell-dependent cd targets

* fix(subagents): reject shell-dependent runner paths

* fix(subagents): harden cross-shell acceptance checks

* fix(subagents): reject ambiguous shell tokenization

* fix(subagents): reject tokenizer segment drift

* fix(subagents): reject ambiguous PowerShell syntax

* fix(subagents): include all PowerShell quote delimiters

* fix(subagents): fail closed on cross-family paths

* fix(subagents): reject ambiguous Windows aliases

* fix(subagents): reject PSDrive alias exclusions

* fix(subagents): reject PSDrive-relative aliases

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
shawn 2026-09-12 09:28:46 +08:00 committed by GitHub
parent 9f4a7823e2
commit bec0acf6b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 1093 additions and 26 deletions

View File

@ -261,9 +261,8 @@ make test-live
PYTHONPATH=. uv run pytest tests/test_<feature>.py -v
```
Direct pytest collection or execution of `tests/test_client_live.py` remains
skipped unless `DEER_FLOW_RUN_LIVE_TESTS=1` is set. Do not add that opt-in to
default CI workflows.
Keep live tests opt-in via `DEER_FLOW_RUN_LIVE_TESTS=1`; guard POSIX-only
markers with `os.name` for Windows collection.
Jina logging tests use dummy keys (`tests/test_jina_client.py`).
Jina/Browserless/InfoQuest resolve URLs without rebuilding HTML.

File diff suppressed because one or more lines are too long

View File

@ -55,10 +55,13 @@ the async caller offloads the whole check with ``asyncio.to_thread``.
from __future__ import annotations
import ntpath
import os
import posixpath
import re
import shlex
import stat
import unicodedata
from collections.abc import Callable, Mapping
from typing import Any, TypedDict
@ -480,6 +483,39 @@ def _check_file_leaf(
_SHELL_OPERATORS = ";&|"
#: Leading ``VAR=value`` assignments are environment setup, not the executable.
_ENV_ASSIGNMENT_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*=")
_WINDOWS_DRIVE_QUALIFIED_RE = re.compile(r"^[A-Za-z]:")
_WINDOWS_DRIVE_ABSOLUTE_RE = re.compile(r"^[A-Za-z]:/")
_WINDOWS_UNC_ABSOLUTE_RE = re.compile(r"^//[^/]+/[^/]+(?:/|$)")
#: A generated 8.3 component has at most six legal stem characters before a
#: one-digit ``~N`` tail and may retain an extension of at most three legal
#: characters. Lexical normalization cannot prove that it differs from the
#: corresponding long name on a volume where short-name generation is active.
_WINDOWS_SHORT_NAME_COMPONENT_RE = re.compile(r"^[A-Za-z0-9$%_'@~`!(){}^#&-]{1,6}~[1-9](?:\.[A-Za-z0-9$%_'@~`!(){}^#&-]{1,3})?$")
#: PowerShell paths may name a provider/PSDrive before ``:`` (for example,
#: ``FileSystem::C:/tmp`` or ``External:/tmp``). Those forms are absolute in
#: PowerShell but look relative to POSIX path normalization.
_POWERSHELL_DRIVE_QUALIFIED_RE = re.compile(r"^[^/\\:]+:")
#: A provider-qualified filesystem path carries either a drive designator or
#: a UNC root after PowerShell's ``Provider::`` prefix. Keep that delimiter
#: distinct from pytest's later ``::nodeid`` separator.
_POWERSHELL_PROVIDER_QUALIFIED_RE = re.compile(r"^[^/\\:]+::(?P<path>(?:[^/\\:]+:|//))")
#: Unlike drive-letter paths, a named PSDrive may have a multi-character name.
#: A separator after the colon distinguishes root-anchored from drive-relative
#: spellings whose resolution depends on that PSDrive's remembered location.
_POWERSHELL_DRIVE_ABSOLUTE_RE = re.compile(r"^[^/\\:]+:/")
#: ``cmd.exe`` expands paired-percent environment references before running
#: the command. Without shell provenance, a token such as ``%TEMP%`` cannot
#: be treated as the literal relative path seen by the POSIX parser.
_CMD_ENV_EXPANSION_RE = re.compile(r"%[^%\r\n]+%")
_CMD_DELAYED_ENV_EXPANSION_RE = re.compile(r"![^!\r\n]+!")
#: Shells supported by ``LocalSandbox`` do not agree on Bash brace expansion.
#: The evidence currently records no shell kind, so matching must reject it
#: before a POSIX parser can turn the expression into one harmless-looking
#: token.
_BASH_BRACE_EXPANSION_RE = re.compile(r"\{[^{}\r\n]*(?:,|\.\.)[^{}\r\n]*\}")
#: PowerShell treats seven typographic single and double quotes as string
#: delimiters, while POSIX ``shlex`` retains them as ordinary token characters.
_POWERSHELL_QUOTE_DELIMITERS = frozenset("‘’‚‛“”„")
def _carries_summary_shape(text: str) -> bool:
@ -490,6 +526,58 @@ def _carries_summary_shape(text: str) -> bool:
return bool(_TEST_PASS_SHAPE_RE.search(text) or _TEST_FAIL_SHAPE_RE.search(text) or _TEST_ZERO_SHAPE_RE.search(text))
def _normalize_cd_scope_path(path: str) -> tuple[str, bool] | None:
"""Normalize a ``cd`` path and retain Windows-absolute provenance.
``posixpath.normpath`` drops the slash from a bare Windows drive root and
collapses traversal above that root into a relative-looking path. Handle
drive-qualified absolute and UNC paths component by component so those
spellings remain absolute, and fail closed when ``..`` would cross the
drive or share root. Drive-relative forms such as ``C:tmp`` also fail
closed because their resolution depends on the process's remembered
directory for that drive.
"""
slash_path = path.replace("\\", "/")
if slash_path.startswith(("//?/", "//./")):
# Win32 device namespaces can alias ordinary drive/UNC paths, but the
# lexical checker has no filesystem provenance to resolve them.
return None
if _POWERSHELL_DRIVE_QUALIFIED_RE.match(slash_path) and not _WINDOWS_DRIVE_QUALIFIED_RE.match(slash_path):
return None
if _WINDOWS_DRIVE_QUALIFIED_RE.match(slash_path) and not _WINDOWS_DRIVE_ABSOLUTE_RE.match(slash_path):
return None
if _WINDOWS_UNC_ABSOLUTE_RE.match(slash_path):
unc_parts = slash_path[2:].split("/")
share_root = unc_parts[:2]
parts: list[str] = []
for part in unc_parts[2:]:
if not part or part == ".":
continue
if part == "..":
if not parts:
return None
parts.pop()
else:
parts.append(part)
normalized = "//" + "/".join((*share_root, *parts))
return normalized, True
if not _WINDOWS_DRIVE_ABSOLUTE_RE.match(slash_path):
return posixpath.normpath(slash_path), False
parts: list[str] = []
for part in slash_path[3:].split("/"):
if not part or part == ".":
continue
if part == "..":
if not parts:
return None
parts.pop()
else:
parts.append(part)
normalized = slash_path[:2] + "/" + "/".join(parts)
return normalized, True
def _cd_target_in_scope(target: str, thread_data: Mapping[str, Any] | None) -> bool:
"""Whether a preceding ``cd`` target provably keeps the criterion's
relative path-like targets resolving inside the thread's data roots.
@ -508,16 +596,38 @@ def _cd_target_in_scope(target: str, thread_data: Mapping[str, Any] | None) -> b
"""
if not target or target == "-" or target.startswith("~"):
return False
normalized = os.path.normpath(target.replace("\\", "/"))
if normalized.startswith("/"):
if ".." in target.replace("\\", "/").split("/"):
# Lexical cleanup is not proof of containment: the OS follows a
# directory symlink before resolving the following ``..`` component.
return False
# After shell-dependent spellings have failed closed, keep deterministic
# POSIX lexical semantics even when this checker itself runs on Windows.
normalized_target = _normalize_cd_scope_path(target)
if normalized_target is None:
return False
normalized, is_windows_absolute = normalized_target
if _thread_uses_windows_paths(thread_data) and normalized.startswith("/") and not is_windows_absolute:
# ``/mnt/...`` is absolute under Bash but drive-rooted under
# PowerShell. Without the executing shell, a Windows workspace cannot
# prove that the virtual POSIX spelling stayed inside its data root.
return False
if normalized.startswith("/") or is_windows_absolute:
roots = [VIRTUAL_PATH_PREFIX]
for key in ("workspace_path", "outputs_path", "uploads_path"):
value = (thread_data or {}).get(key)
if isinstance(value, str) and value:
roots.append(value)
for root in roots:
normalized_root = os.path.normpath(root.replace("\\", "/"))
if normalized == normalized_root or normalized.startswith(normalized_root + "/"):
normalized_root_result = _normalize_cd_scope_path(root)
if normalized_root_result is None:
continue
normalized_root, root_is_windows_absolute = normalized_root_result
use_windows_comparison = is_windows_absolute and root_is_windows_absolute
candidate_for_comparison = ntpath.normcase(normalized) if use_windows_comparison else normalized
root_for_comparison = ntpath.normcase(normalized_root) if use_windows_comparison else normalized_root
separator = "\\" if use_windows_comparison else "/"
root_prefix = root_for_comparison if root_for_comparison.endswith(separator) else root_for_comparison + separator
if candidate_for_comparison == root_for_comparison or candidate_for_comparison.startswith(root_prefix):
return True
return False
return ".." not in normalized.split("/")
@ -605,26 +715,175 @@ def _negated_value(token: str) -> str:
return token
def _negation_overlaps(criterion_token: str, negated_value: str) -> bool:
def _thread_uses_windows_paths(thread_data: Mapping[str, Any] | None) -> bool:
"""Whether the thread roots establish Windows filesystem semantics."""
for key in ("workspace_path", "outputs_path", "uploads_path"):
value = (thread_data or {}).get(key)
if not isinstance(value, str):
continue
slash_path = value.replace("\\", "/")
if _WINDOWS_DRIVE_ABSOLUTE_RE.match(slash_path) or _WINDOWS_UNC_ABSOLUTE_RE.match(slash_path):
return True
return False
def _selection_path_parts(value: str) -> tuple[str, str | None]:
"""Split a runner selection into its filesystem path and pytest nodeid."""
provider_match = _POWERSHELL_PROVIDER_QUALIFIED_RE.match(value)
nodeid_start = provider_match.end() if provider_match is not None else 0
marker_index = value.find("::", nodeid_start)
if marker_index < 0:
return value, None
return value[:marker_index], value[marker_index + 2 :]
def _without_powershell_provider(path: str) -> str:
"""Return the rooted portion of a provider-qualified filesystem path."""
provider_match = _POWERSHELL_PROVIDER_QUALIFIED_RE.match(path)
if provider_match is None:
return path
return path[provider_match.start("path") :]
def _has_parent_path_component(value: str) -> bool:
path, _nodeid = _selection_path_parts(value)
return ".." in path.replace("\\", "/").split("/")
def _uses_windows_selection_semantics(value: str, *, windows_path_context: bool) -> bool:
path, _nodeid = _selection_path_parts(value)
slash_path = _without_powershell_provider(path).replace("\\", "/")
return bool(windows_path_context or _POWERSHELL_DRIVE_QUALIFIED_RE.match(slash_path) or _WINDOWS_UNC_ABSOLUTE_RE.match(slash_path))
def _selection_path_kind(value: str) -> str:
path, _nodeid = _selection_path_parts(value)
slash_path = _without_powershell_provider(path).replace("\\", "/")
if _WINDOWS_DRIVE_ABSOLUTE_RE.match(slash_path):
return "windows_drive_absolute"
if _WINDOWS_UNC_ABSOLUTE_RE.match(slash_path):
return "windows_unc_absolute"
if slash_path.startswith("/"):
return "posix_absolute"
if _POWERSHELL_DRIVE_QUALIFIED_RE.match(slash_path) and not _WINDOWS_DRIVE_QUALIFIED_RE.match(slash_path):
if _POWERSHELL_DRIVE_ABSOLUTE_RE.match(slash_path):
return "powershell_drive_absolute"
return "powershell_drive_relative"
return "relative"
def _windows_volume_identifier(value: str) -> tuple[str, str] | None:
"""Return a comparable drive, PSDrive, or UNC-root identifier."""
path, _nodeid = _selection_path_parts(value)
slash_path = _without_powershell_provider(path).replace("\\", "/")
if _WINDOWS_DRIVE_QUALIFIED_RE.match(slash_path):
return "drive", ntpath.normcase(slash_path[:2])
if _WINDOWS_UNC_ABSOLUTE_RE.match(slash_path):
server, share, *_rest = slash_path[2:].split("/")
return "unc", ntpath.normcase(f"//{server}/{share}")
if _POWERSHELL_DRIVE_QUALIFIED_RE.match(slash_path):
drive, _separator, _rest = slash_path.partition(":")
return "psdrive", ntpath.normcase(drive)
return None
def _has_ambiguous_windows_component(value: str, *, windows_path_context: bool) -> bool:
"""Whether ordinary Win32 cleanup may alias a textual path component.
Windows APIs normally discard trailing spaces and periods from path
components, while extended-length paths can preserve them. Generated 8.3
short names can also identify a longer component without any lexical
relationship. Without shell and filesystem provenance, these spellings
must make an overlap decision fail closed.
"""
if not _uses_windows_selection_semantics(value, windows_path_context=windows_path_context):
return False
path, _nodeid = _selection_path_parts(value)
path = _without_powershell_provider(path)
return any(component not in {"", ".", ".."} and (component.endswith((" ", ".")) or _WINDOWS_SHORT_NAME_COMPONENT_RE.fullmatch(component)) for component in path.replace("\\", "/").split("/"))
def _normalize_selection_path(value: str, *, windows_path_context: bool) -> tuple[str, str | None, bool]:
"""Normalize safe lexical aliases without erasing pytest nodeid case.
Parent traversal is rejected by the caller because resolving ``..``
lexically can cross symlinks. ``.`` and duplicate separators are safe to
collapse. Drive-qualified and UNC paths always use ``ntpath`` semantics;
when the thread roots establish a Windows workspace, every runner path
does, including drive-rooted ``/tests`` and mapped virtual paths.
"""
path, nodeid = _selection_path_parts(value)
slash_path = _without_powershell_provider(path).replace("\\", "/")
is_windows_path = _uses_windows_selection_semantics(value, windows_path_context=windows_path_context)
if is_windows_path:
normalized = ntpath.normcase(ntpath.normpath(slash_path))
else:
normalized = posixpath.normpath(slash_path)
return normalized, nodeid, is_windows_path
def _nodeids_overlap(a: str | None, b: str | None) -> bool:
if a is None or b is None:
return True
return a == b or a.startswith(b + "::") or b.startswith(a + "::")
def _negation_overlaps(criterion_token: str, negated_value: str, *, windows_path_context: bool = False) -> bool:
"""Whether a negated value overlaps a matched criterion target: equal, or
one nested under the other at a path boundary (``tests`` vs
``tests/unit/test_auth.py``) or a pytest nodeid boundary (``tests/x.py``
vs ``tests/x.py::test_y``). Overlap means part of the criterion's
selection never ran, so the passing summary may not cover it; unrelated
exclusions (``--ignore tests/slow`` against ``pytest tests/unit``) do not
overlap and keep matching."""
a = criterion_token.replace("\\", "/").removeprefix("./").rstrip("/")
b = negated_value.replace("\\", "/").removeprefix("./").rstrip("/")
if not a or not b:
overlap and keep matching. Safe lexical aliases are normalized, while
mixed path families fail closed because evidence does not carry enough
filesystem provenance to prove that their spellings are distinct."""
a_kind = _selection_path_kind(criterion_token)
b_kind = _selection_path_kind(negated_value)
if a_kind != b_kind:
# Relative selections resolve against an unrecorded cwd, and Windows
# can resolve POSIX-rooted spellings against the current drive or map
# a drive onto a UNC share. Cross-family spellings can therefore alias
# even when their lexical prefixes differ.
return True
a_volume = _windows_volume_identifier(criterion_token)
b_volume = _windows_volume_identifier(negated_value)
if a_volume is not None and b_volume is not None and a_volume != b_volume:
# Distinct drive letters can alias through SUBST or mapped drives, and
# distinct UNC roots can alias through DFS, DNS, or share mappings.
# The execution evidence records none of that volume provenance.
return True
if a_kind == "relative":
a_path, _a_nodeid = _selection_path_parts(criterion_token)
b_path, _b_nodeid = _selection_path_parts(negated_value)
a_is_drive_relative = bool(_WINDOWS_DRIVE_QUALIFIED_RE.match(a_path.replace("\\", "/")))
b_is_drive_relative = bool(_WINDOWS_DRIVE_QUALIFIED_RE.match(b_path.replace("\\", "/")))
if a_is_drive_relative != b_is_drive_relative:
# ``tests/x`` and ``D:tests/x`` can name the same path when the
# process cwd and D:'s remembered cwd coincide. Neither value is
# absolute, and the execution evidence carries neither cwd.
return True
a, a_nodeid, a_is_windows = _normalize_selection_path(criterion_token, windows_path_context=windows_path_context)
b, b_nodeid, b_is_windows = _normalize_selection_path(negated_value, windows_path_context=windows_path_context)
if not a or not b or a == "." or b == ".":
return False
return a == b or a.startswith((b + "/", b + "::")) or b.startswith((a + "/", a + "::"))
if a == b:
return _nodeids_overlap(a_nodeid, b_nodeid)
if a_is_windows != b_is_windows:
# A drive-relative spelling can resolve to the same path as an
# ordinary relative token, but the per-drive cwd is not recorded.
return True
separator = "\\" if a_is_windows else "/"
a_prefix = a if a.endswith(separator) else a + separator
b_prefix = b if b.endswith(separator) else b + separator
return a.startswith(b_prefix) or b.startswith(a_prefix)
def _normalize_command(command: str) -> str:
return " ".join(command.split())
def _shell_parse_line(line: str) -> tuple[str | None, list[list[str]], list[str]] | None:
def _shell_parse_line(line: str, *, posix: bool = True) -> tuple[str | None, list[list[str]], list[str]] | None:
"""Tokenize one physical line into segments plus the operators joining them.
Returns ``(leading_op, segments, ops)``: ``ops[i]`` is the operator
@ -636,9 +895,11 @@ def _shell_parse_line(line: str) -> tuple[str | None, list[list[str]], list[str]
``;`` would overstate what provably ran. Comments are stripped (a
``# pytest ...`` remark executes nothing) and quotes are honored, so an
operator inside an argument cannot split a segment. Returns ``None`` on
malformed shell (unbalanced quotes).
malformed shell (unbalanced quotes). ``posix=False`` is reserved for the
raw-token safety pass: it keeps backslashes and surrounding quotes visible
before the normal POSIX parse can consume them as escaping syntax.
"""
lexer = shlex.shlex(line, posix=True, punctuation_chars=_SHELL_OPERATORS)
lexer = shlex.shlex(line, posix=posix, punctuation_chars=_SHELL_OPERATORS)
lexer.whitespace_split = True
lexer.commenters = "#"
try:
@ -670,7 +931,7 @@ def _shell_parse_line(line: str) -> tuple[str | None, list[list[str]], list[str]
return leading_op, segments, ops
def _shell_parse(command: str) -> tuple[list[list[str]], list[str]] | None:
def _shell_parse(command: str, *, posix: bool = True) -> tuple[list[list[str]], list[str]] | None:
"""Tokenize a shell command into segments plus the operators joining them.
Physical newlines are command separators with ``;`` semantics bash
@ -692,7 +953,7 @@ def _shell_parse(command: str) -> tuple[list[list[str]], list[str]] | None:
segments: list[list[str]] = []
ops: list[str] = []
for line in command.split("\n"):
parsed = _shell_parse_line(line)
parsed = _shell_parse_line(line, posix=posix)
if parsed is None:
return None
leading_op, line_segments, line_ops = parsed
@ -709,6 +970,142 @@ def _shell_parse(command: str) -> tuple[list[list[str]], list[str]] | None:
return segments, ops
def _has_shell_ambiguous_whitespace(command: str) -> bool:
"""Whether whitespace can split differently across supported shells.
ASCII space, tab, LF, and CRLF are shared separators. PowerShell also
separates on bare CR, vertical tab, form feed, NEL, and Unicode separator
characters, while Python's POSIX ``shlex`` can retain them inside a token.
"""
command_without_crlf = command.replace("\r\n", "\n")
return any(character in "\r\v\f\x85" or (character != " " and unicodedata.category(character) in {"Zs", "Zl", "Zp"}) for character in command_without_crlf)
def _has_cmd_control_operator_in_single_quotes(command: str) -> bool:
"""Whether POSIX single quotes hide cmd.exe control syntax.
Cmd does not use single quotes for grouping, so metacharacters within them
remain active there. Ordinary single-quoted text stays verifiable for the
POSIX execution path. Shell-specific ways to escape quotes are already
rejected by the other provenance checks below.
"""
in_double_quotes = False
in_single_quotes = False
for character in command:
if character == '"':
in_double_quotes = not in_double_quotes
elif character == "'" and not in_double_quotes:
in_single_quotes = not in_single_quotes
elif in_single_quotes and character in "&|<>()":
return True
return False
def _has_unquoted_parenthesis(command: str) -> bool:
"""Whether a parenthesis appears outside a quoted string.
PowerShell evaluates an unquoted parenthesized command expression and
expands its output into native arguments. POSIX tokenization instead
leaves the parentheses attached to ordinary tokens, which can hide an
injected runner option. Parentheses inside double- or single-quoted
strings are data; cmd-specific control syntax inside single quotes is
rejected separately because cmd does not honor those quotes.
"""
in_double_quotes = False
in_single_quotes = False
for character in command:
if character == '"' and not in_single_quotes:
in_double_quotes = not in_double_quotes
elif character == "'" and not in_double_quotes:
in_single_quotes = not in_single_quotes
elif character in "()" and not in_double_quotes and not in_single_quotes:
return True
return False
def _has_unquoted_single_quote(text: str) -> bool:
"""Whether *text* contains a single quote outside double quotes.
The raw ``posix=False`` token stream retains surrounding quotes. Cmd.exe
passes a POSIX single quote to the child process instead of using it for
grouping, so such a token cannot be compared with its quote-stripped
POSIX form without knowing which shell executed it.
"""
in_double_quotes = False
for character in text:
if character == '"':
in_double_quotes = not in_double_quotes
elif character == "'" and not in_double_quotes:
return True
return False
def _raw_segment_has_unquoted_single_quote(segment: list[str]) -> bool:
return any(_has_unquoted_single_quote(token) for token in segment)
def _matched_span_has_ambiguous_single_quotes(
expected_raw: list[list[str]],
actual_raw: list[list[str]],
actual: list[list[str]],
*,
start: int,
span: int,
thread_data: Mapping[str, Any] | None,
) -> bool:
"""Whether cmd.exe single-quote semantics can change a candidate match.
Every expected segment and the matching actual span define the invocation
being certified, so a POSIX single quote in any of them is ambiguous.
Before the span, only segments accepted as provably silent can contribute
to a successful match; a quoted ``cd`` or assignment can change the state
in which the runner executes and therefore also requires shell provenance.
Non-silent prefixes remain governed by output-attribution checks, preserving
authoritative failure results such as ``echo '12 passed'; make test``.
"""
if any(_raw_segment_has_unquoted_single_quote(segment) for segment in expected_raw):
return True
if any(_raw_segment_has_unquoted_single_quote(segment) for segment in actual_raw[start : start + span]):
return True
return any(_raw_segment_has_unquoted_single_quote(actual_raw[index]) and _is_silent_segment(actual[index], thread_data) for index in range(start))
def _command_requires_shell_provenance(command: str) -> bool:
"""Whether raw command tokens have shell-dependent Windows semantics.
The acceptance evidence does not currently record which shell executed a
command. Inspect a non-POSIX tokenization before the authoritative POSIX
parser can discard backslashes anywhere in the candidate command: PowerShell
and native Windows runners preserve them as path separators while POSIX
shells treat them as escapes. Cmd percent/bang references and ``^``, Bash
tilde/brace expansion, and PowerShell splatting, typographic quotes, or
unquoted parentheses can all rewrite the native argv differently. ``#``
starts a comment for the POSIX parser but is an ordinary argument to
cmd.exe. Cmd also does not treat single quotes as quoting, so cmd control
syntax hidden inside POSIX single quotes is unsafe. PowerShell recognizes
more separators than POSIX ``shlex``, including bare carriage returns and
Unicode separator characters; normal CRLF remains unambiguous. These forms
fail closed rather than certifying arguments that depend on an unknown
shell.
"""
if (
_has_shell_ambiguous_whitespace(command)
or _has_cmd_control_operator_in_single_quotes(command)
or "#" in command
or "^" in command
or _CMD_DELAYED_ENV_EXPANSION_RE.search(command)
or _BASH_BRACE_EXPANSION_RE.search(command)
or any(quote in command for quote in _POWERSHELL_QUOTE_DELIMITERS)
or _has_unquoted_parenthesis(command)
):
return True
parsed = _shell_parse(command, posix=False)
if parsed is None:
return "\\" in command or bool(_CMD_ENV_EXPANSION_RE.search(command))
segments, _ops = parsed
return any("\\" in token or token.startswith(("~", "@")) or (token.startswith("(") and token.endswith(")")) or _CMD_ENV_EXPANSION_RE.search(token) for segment in segments for token in segment)
def _strip_env_assignments(tokens: list[str]) -> list[str]:
index = 0
while index < len(tokens) and _ENV_ASSIGNMENT_RE.match(tokens[index]):
@ -878,7 +1275,7 @@ def _normalize_executable(token: str) -> str:
return os.path.normpath(token.replace("\\", "/"))
def _segment_matches(expected: list[str], actual: list[str]) -> str:
def _segment_matches(expected: list[str], actual: list[str], *, windows_path_context: bool = False) -> str:
"""Match one segment against the criterion's, classifying extra flags.
Returns ``"match"`` when the executable agrees directional: a bare
@ -1003,7 +1400,14 @@ def _segment_matches(expected: list[str], actual: list[str]) -> str:
# before resolving ``..``, so a textually unrelated value can name
# the criterion's target).
return "unprovable"
if any(_negation_overlaps(actual[position], value) for position in consumed if position != 0 for value in negated_values):
compared_values = [actual[position] for position in consumed if position != 0] + negated_values
if negated_values and any(_has_ambiguous_windows_component(value, windows_path_context=windows_path_context) for value in compared_values):
return "unprovable"
if negated_values and any(_has_parent_path_component(actual[position]) for position in consumed if position != 0):
# The matched target itself may normalize onto an excluded path, but
# collapsing its ``..`` components would be unsound across symlinks.
return "unprovable"
if any(_negation_overlaps(actual[position], value, windows_path_context=windows_path_context) for position in consumed if position != 0 for value in negated_values):
return "unprovable"
for position, token in enumerate(actual):
if position in consumed or position in negated or position in option_positions:
@ -1096,7 +1500,13 @@ def _criterion_connectors_preserved(expected_ops: list[str], executed_within_ops
return True
def _commands_match(criterion_command: str, executed_command: str, *, executed_success: bool) -> str:
def _commands_match(
criterion_command: str,
executed_command: str,
*,
executed_success: bool,
thread_data: Mapping[str, Any] | None = None,
) -> str:
"""Shell-structure match with control-flow attribution.
Returns ``"match"`` when the criterion's segment sequence appears as
@ -1110,23 +1520,35 @@ def _commands_match(criterion_command: str, executed_command: str, *, executed_s
"""
expected_parsed = _shell_parse(criterion_command)
actual_parsed = _shell_parse(executed_command)
expected_raw_parsed = _shell_parse(criterion_command, posix=False)
actual_raw_parsed = _shell_parse(executed_command, posix=False)
needs_shell_provenance = _command_requires_shell_provenance(criterion_command) or _command_requires_shell_provenance(executed_command)
if expected_parsed is None or actual_parsed is None:
# Malformed shell: only exact normalized equality survives.
expected_norm = _normalize_command(criterion_command)
return "match" if expected_norm and expected_norm == _normalize_command(executed_command) else "no_match"
if not expected_norm or expected_norm != _normalize_command(executed_command):
return "no_match"
has_ambiguous_single_quote = _has_unquoted_single_quote(criterion_command) or _has_unquoted_single_quote(executed_command)
return "unprovable" if needs_shell_provenance or has_ambiguous_single_quote else "match"
expected, expected_ops = expected_parsed
actual, ops = actual_parsed
if not expected or not actual or len(expected) > len(actual):
return "no_match"
raw_segments_align = expected_raw_parsed is not None and actual_raw_parsed is not None and len(expected_raw_parsed[0]) == len(expected) and len(actual_raw_parsed[0]) == len(actual)
if not raw_segments_align:
needs_shell_provenance = True
expected_raw = expected_raw_parsed[0] if raw_segments_align and expected_raw_parsed is not None else []
actual_raw = actual_raw_parsed[0] if raw_segments_align and actual_raw_parsed is not None else []
span = len(expected)
saw_unprovable = False
windows_path_context = _thread_uses_windows_paths(thread_data)
for start in range(len(actual) - span + 1):
if any(_segment_pollutes_state(segment) for segment in actual[:start]):
# A preceding segment mutated shell state the matcher cannot see
# (PATH/exports): nothing later is provable.
saw_unprovable = True
continue
outcomes = [_segment_matches(expected[i], actual[start + i]) for i in range(span)]
outcomes = [_segment_matches(expected[i], actual[start + i], windows_path_context=windows_path_context) for i in range(span)]
if any(outcome == "no_match" for outcome in outcomes):
continue
if any(outcome == "unprovable" for outcome in outcomes):
@ -1143,6 +1565,16 @@ def _commands_match(criterion_command: str, executed_command: str, *, executed_s
saw_unprovable = True
continue
if _span_attributable(ops[:start], ops[start : start + span - 1], ops[start + span - 1 :], executed_success):
if needs_shell_provenance or _matched_span_has_ambiguous_single_quotes(
expected_raw,
actual_raw,
actual,
start=start,
span=span,
thread_data=thread_data,
):
saw_unprovable = True
continue
return "match"
saw_unprovable = True
return "unprovable" if saw_unprovable else "no_match"
@ -1190,7 +1622,12 @@ def _check_tests_passed_leaf(command: str, bash_executions: list[dict[str, Any]]
matches: list[tuple[str, dict[str, Any]]] = []
for execution in bash_executions or []:
status = str(execution.get("status") or "")
outcome = _commands_match(command, str(execution.get("command") or ""), executed_success=status == "success")
outcome = _commands_match(
command,
str(execution.get("command") or ""),
executed_success=status == "success",
thread_data=thread_data,
)
if outcome == "match" and execution.get("command_truncated"):
# The recorded command lost its suffix to the evidence cap; a
# selection-changing tail (``-k smoke``) may have been cut away.

View File

@ -24,6 +24,12 @@ THREAD_DATA = {
"outputs_path": "/ws/thread/user-data/outputs",
}
WINDOWS_THREAD_DATA = {
"workspace_path": "D:/WS",
"uploads_path": "D:/WS/uploads",
"outputs_path": "D:/WS/outputs",
}
def _reader(files: dict[str, str]):
def read(_runtime, path: str) -> str:
@ -124,7 +130,7 @@ class TestFileLeaves:
from deerflow.sandbox.tools import _resolve_local_read_path
assert Path(_resolve_local_read_path(seen[0], THREAD_DATA)) == Path("/ws/thread/user-data/outputs/report.md") # type: ignore[arg-type]
assert Path(_resolve_local_read_path(seen[0], THREAD_DATA)) == Path("/ws/thread/user-data/outputs/report.md").resolve() # type: ignore[arg-type]
def test_non_empty_fails_on_empty_file(self):
files = {"/mnt/user-data/outputs/report.md": ""}
@ -656,7 +662,7 @@ class TestProbeInnerScriptRealLayouts:
(outputs / "report.md").write_text("hello", encoding="utf-8")
assert self._run_read_probe(str(outputs / "report.md"), str(outputs)) == "READABLE"
@pytest.mark.skipif(os.geteuid() == 0, reason="root reads through mode-000")
@pytest.mark.skipif(os.name == "nt" or os.geteuid() == 0, reason="mode-000 readability needs POSIX permissions and a non-root euid")
def test_read_probe_mode_000_is_unreadable(self, tmp_path):
outputs = tmp_path / "outputs"
outputs.mkdir()
@ -1156,6 +1162,120 @@ class TestTestsPassedLeaf:
assert verdict["leaves"][0]["holds"] is True
def test_crlf_execution_with_test_command_last_still_matches(self):
"""A normal Windows CRLF command record has the same boundaries as
its LF equivalent and must not require shell provenance."""
executions = [_bash_execution("cd backend\r\npytest tests/", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/"], bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_bare_carriage_return_is_unprovable_before_powershell_line_split(self):
"""PowerShell treats a standalone CR as a command boundary, while a
POSIX parser may absorb it as whitespace and attribute a forged
passing summary to the preceding test run."""
executions = [_bash_execution("pytest tests/security\rWrite-Output '3 passed'", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/security"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
@pytest.mark.parametrize("separator", ("\v", "\f", "\x85", "\u00a0", "\u2028", "\u2029"))
def test_powershell_only_whitespace_cannot_hide_runner_arguments(self, separator):
"""PowerShell separates arguments on these characters, while POSIX
shlex can absorb them into one apparently harmless token."""
command = f"pytest tests/security tests/unit{separator}--ignore{separator}tests/security"
executions = [_bash_execution(command, output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/security"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_cmd_single_quote_cannot_hide_a_control_operator(self):
"""cmd.exe does not quote ``&`` with single quotes, even though POSIX
shlex would hide the operator inside one argument."""
executions = [_bash_execution("pytest tests/security '& echo 3 passed'", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/security"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_cmd_single_quotes_cannot_change_matched_runner_argv(self):
"""POSIX removes ordinary single quotes, while cmd.exe passes them
through as part of the runner argument."""
executions = [_bash_execution("pytest 'tests/security'", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/security"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_cmd_single_quotes_in_criterion_are_unprovable(self):
executions = [_bash_execution("pytest tests/security", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest 'tests/security'"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_cmd_single_quotes_in_preceding_cd_are_unprovable(self):
"""A quoted POSIX ``cd`` target is not the same path under cmd.exe,
so it cannot establish the runner's working directory."""
executions = [_bash_execution("cd '/mnt/user-data/workspace' && pytest tests/security", output_tail="3 passed")]
verdict = check_acceptance_criteria(
["tests_passed:pytest tests/security"],
thread_data=THREAD_DATA,
bash_executions=executions,
)
assert verdict["leaves"][0]["checked"] is False
def test_mismatched_posix_and_raw_segments_are_unprovable(self):
"""A tokenizer disagreement must fail closed instead of indexing a
raw segment list that could not be produced."""
executions = [_bash_execution('cd foo";"bar && pytest tests/', output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/"], bash_executions=executions)
leaf = verdict["leaves"][0]
assert leaf["checked"] is False
assert leaf["holds"] is False
def test_powershell_parenthesized_expression_cannot_inject_runner_arguments(self):
"""PowerShell expands a parenthesized command expression into native
arguments, while POSIX tokenization leaves the closing parenthesis on
the exclusion path and can miss that the required target was skipped."""
command = 'pytest tests/security tests/unit (Write-Output "--ignore" "tests/security")'
executions = [_bash_execution(command, output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/security"], bash_executions=executions)
leaf = verdict["leaves"][0]
assert leaf["checked"] is False
assert leaf["holds"] is False
@pytest.mark.parametrize(
("opening_quote", "closing_quote"),
[("", ""), ("", ""), ("", ""), ("", ""), ("", "")],
)
def test_powershell_quote_delimiters_cannot_hide_runner_exclusions(self, opening_quote: str, closing_quote: str):
command = f"pytest tests/security tests/unit {opening_quote}--deselect=tests/security/test_auth.py::test_required{closing_quote}"
executions = [_bash_execution(command, output_tail="3 passed, 1 deselected")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/security"], bash_executions=executions)
leaf = verdict["leaves"][0]
assert leaf["checked"] is False
assert leaf["holds"] is False
def test_shared_whitespace_and_double_quotes_remain_verifiable(self):
executions = [_bash_execution('pytest\t"tests/security" -q', output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/security"], bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_parentheses_inside_double_quoted_path_remain_verifiable(self):
executions = [_bash_execution('pytest "tests/(security)"', output_tail="3 passed")]
verdict = check_acceptance_criteria(['tests_passed:pytest "tests/(security)"'], bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_apostrophe_inside_double_quotes_remains_verifiable(self):
executions = [_bash_execution('pytest "tests/O\'Brien" -q', output_tail="3 passed")]
verdict = check_acceptance_criteria(['tests_passed:pytest "tests/O\'Brien"'], bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_multiline_background_operator_stays_unprovable(self):
"""A trailing ``&`` at end of a line still separates (and backgrounds)
the next line's command."""
@ -1190,6 +1310,151 @@ class TestTestsPassedLeaf:
assert verdict["leaves"][0]["checked"] is False
@pytest.mark.parametrize(
"target",
(
r"..\..\tmp",
r"'\mnt\user-data\workspace\fake'",
),
)
def test_cd_with_backslashes_is_unprovable_before_posix_tokenization(self, target):
"""Shell provenance is absent, so a backslash-bearing ``cd`` target
cannot be interpreted safely as either POSIX escaping or a Windows
path separator."""
executions = [_bash_execution(f"cd {target} && pytest tests/", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/"], thread_data=THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_runner_backslash_negation_is_unprovable_before_posix_tokenization(self):
"""PowerShell preserves the separator in ``tests\\security``, while
POSIX ``shlex`` consumes it and could hide that the required target was
excluded from the recorded run."""
executions = [_bash_execution(r"pytest tests/security tests/unit --ignore tests\security", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/security"], thread_data=THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_cmd_hash_argument_is_unprovable_before_comment_stripping(self):
"""``cmd.exe`` passes ``#`` as an ordinary argument, while POSIX
parsing treats it as the start of a comment and can hide a following
exclusion from the acceptance matcher."""
executions = [_bash_execution("pytest tests/security tests/unit # --ignore tests/security", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/security"], thread_data=THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_cmd_caret_escape_is_unprovable_before_posix_tokenization(self):
"""``cmd.exe`` removes ``^`` escaping before invoking the runner, so
the literal token seen by the matcher may hide an exclusion alias."""
executions = [_bash_execution("pytest tests/security tests/unit --ignore tests^/security", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/security"], thread_data=THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
@pytest.mark.parametrize(
"command",
(
"pytest tests/security tests/unit {--ignore,tests/security}",
'pytest tests/security tests/unit @("--ignore","tests/security")',
'pytest tests/security tests/unit ("--ignore","tests/security")',
'pytest tests/security tests/unit @("--ignore=tests/security")',
'pytest tests/security tests/unit ("--ignore=tests/security")',
"pytest tests/security tests/unit @pytestArgs",
"pytest tests/security tests/unit --ignore ~/repo/tests/security",
"pytest tests/security tests/unit --ignore !TARGET!",
),
)
def test_shell_expansion_syntax_is_unprovable_before_tokenization(self, command):
"""Bash brace/tilde expansion and PowerShell splatting/arrays can
inject or rewrite runner arguments before the process is launched."""
executions = [_bash_execution(command, output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/security"], thread_data=WINDOWS_THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
@pytest.mark.parametrize("target", ("%TEMP%", "%USERPROFILE%/fake"))
def test_cd_with_cmd_environment_expansion_is_unprovable(self, target):
executions = [_bash_execution(f"cd {target} && pytest tests/", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/"], thread_data=THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_cd_with_unpaired_percent_remains_a_literal_relative_path(self):
executions = [_bash_execution("cd reports/100% && pytest tests/", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/"], thread_data=THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_cd_to_out_of_scope_windows_drive_path_is_unprovable(self):
thread_data = {
"workspace_path": "D:/ws/thread/user-data/workspace",
"outputs_path": "D:/ws/thread/user-data/outputs",
}
executions = [_bash_execution("cd D:/tmp/fake && pytest tests/", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/"], thread_data=thread_data, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
@pytest.mark.parametrize("target", ("FileSystem::C:/tmp", "External:/tmp"))
def test_cd_to_powershell_provider_or_psdrive_path_is_unprovable(self, target):
"""Provider-qualified and named-PSDrive paths are absolute in
PowerShell but look relative to the POSIX path normalizer."""
thread_data = {
"workspace_path": "D:/ws/thread/user-data/workspace",
"outputs_path": "D:/ws/thread/user-data/outputs",
}
executions = [_bash_execution(f"cd {target} && pytest tests/", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/"], thread_data=thread_data, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
@pytest.mark.parametrize("target", ("C:", "C:tmp", "C:../tmp"))
def test_cd_to_windows_drive_relative_path_is_unprovable(self, target):
thread_data = {
"workspace_path": "D:/ws/thread/user-data/workspace",
"outputs_path": "D:/ws/thread/user-data/outputs",
}
executions = [_bash_execution(f"cd {target} && pytest tests/", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/"], thread_data=thread_data, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
@pytest.mark.parametrize("target", ("C:/", "C:/../tmp", "D:/ws/../../tmp"))
def test_cd_to_windows_drive_root_or_above_root_is_unprovable(self, target):
thread_data = {
"workspace_path": "D:/ws/thread/user-data/workspace",
"outputs_path": "D:/ws/thread/user-data/outputs",
}
executions = [_bash_execution(f"cd {target} && pytest tests/", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/"], thread_data=thread_data, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_cd_to_in_scope_windows_drive_path_is_case_insensitive(self):
thread_data = {
"workspace_path": "D:/ws/thread/user-data/workspace",
"outputs_path": "D:/ws/thread/user-data/outputs",
}
executions = [_bash_execution("cd d:/WS/thread/user-data/workspace/project && pytest tests/", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/"], thread_data=thread_data, bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_cd_to_windows_unicode_casefold_collision_is_unprovable(self):
thread_data = {"workspace_path": "D:/ws/Straße/workspace"}
executions = [_bash_execution("cd d:/WS/STRASSE/workspace/fake && pytest tests/", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/"], thread_data=thread_data, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_cd_to_child_of_windows_drive_root_is_in_scope(self):
thread_data = {"workspace_path": "D:/"}
executions = [_bash_execution("cd d:/project && pytest tests/", output_tail="3 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/"], thread_data=thread_data, bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
@pytest.mark.parametrize(
"wrapped",
(
@ -1197,6 +1462,7 @@ class TestTestsPassedLeaf:
"cd && pytest tests/", # bare cd goes HOME
"cd - && pytest tests/", # prints OLDPWD
"cd backend/../../x && pytest tests/", # lexical walk-out
"cd linked/../safe && pytest tests/", # ``..`` can cross a directory symlink
"cd /mnt/user-data/../etc && pytest tests/", # normalized escape
"cd /ws/thread/user-data/workspace2 && pytest tests/", # sibling of an allowed root
),
@ -1557,6 +1823,370 @@ class TestTestsPassedLeaf:
assert leaf["checked"] is False
assert leaf["detail"] == "matching segment cannot be proven to have executed"
def test_windows_case_alias_of_target_that_is_excluded_is_unprovable(self):
"""Windows drive paths are case-insensitive for overlap purposes, so
a differently-cased exclusion can still remove the required target."""
executions = [_bash_execution("pytest D:/WS/tests/security D:/WS/tests/unit --ignore d:/ws/tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest D:/WS/tests/security"], bash_executions=executions)
leaf = verdict["leaves"][0]
assert leaf["checked"] is False
assert leaf["detail"] == "matching segment cannot be proven to have executed"
def test_windows_case_alias_exclusion_nested_under_target_is_unprovable(self):
executions = [_bash_execution("pytest D:/WS/tests --ignore d:/ws/tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest D:/WS/tests"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_unrelated_windows_exclusion_keeps_matching(self):
target = "D:/WS/tests/security"
executions = [_bash_execution(f"pytest {target} D:/WS/tests/unit --ignore d:/ws/tests/slow", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], thread_data=WINDOWS_THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_different_absolute_drive_exclusion_fails_closed_without_thread_context(self):
executions = [_bash_execution("pytest D:/ws/tests/security D:/ws/tests/unit --ignore E:/tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest D:/ws/tests/security"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_different_unc_root_exclusion_fails_closed_without_thread_context(self):
target = "//server1/share/tests/security"
executions = [_bash_execution(f"pytest {target} //server1/share/tests/unit --ignore //server2/share/tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_unrelated_exclusion_on_same_unc_root_keeps_matching(self):
target = "//server/share/tests/security"
executions = [_bash_execution(f"pytest {target} //server/share/tests/unit --ignore //server/share/tests/slow", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_different_psdrive_exclusion_fails_closed_without_thread_context(self):
target = "Data:/tests/security"
executions = [_bash_execution(f"pytest {target} Data:/tests/unit --ignore Mirror:/tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_provider_qualified_psdrive_exclusion_fails_closed_without_thread_context(self):
target = "Data:/tests/security"
executions = [_bash_execution(f"pytest {target} Data:/tests/unit --ignore FileSystem::Mirror:/tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_psdrive_relative_exclusion_fails_closed_without_thread_context(self):
target = "Data:/tests/security"
executions = [_bash_execution(f"pytest {target} Data:/tests/unit --ignore Data:tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_provider_qualified_psdrive_relative_exclusion_fails_closed(self):
target = "Data:/tests/security"
executions = [_bash_execution(f"pytest {target} Data:/tests/unit --ignore FileSystem::data:tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_psdrive_absolute_exclusion_fails_closed_for_relative_criterion(self):
target = "Data:tests/security"
executions = [_bash_execution(f"pytest {target} Data:tests/unit --ignore Data:/tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_psdrive_relative_deselect_fails_closed_for_absolute_nodeid(self):
target = "Data:/tests/security/test_x.py::test_a"
executions = [_bash_execution(f"pytest {target} Data:/tests/unit --deselect Data:tests/security/test_x.py", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_unrelated_exclusion_on_same_relative_psdrive_keeps_matching(self):
target = "Data:tests/security"
executions = [_bash_execution(f"pytest {target} Data:tests/unit --ignore data:tests/slow", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_unrelated_exclusion_on_same_psdrive_keeps_matching(self):
target = "Data:/tests/security"
executions = [_bash_execution(f"pytest {target} Data:/tests/unit --ignore data:/tests/slow", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_same_psdrive_case_alias_exclusion_is_unprovable(self):
target = "Data:/tests/security"
executions = [_bash_execution(f"pytest {target} Data:/tests/unit --ignore data:/TESTS/security", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_provider_qualified_path_on_same_psdrive_compares_lexically(self):
target = "Data:/tests/security"
executions = [_bash_execution(f"pytest {target} Data:/tests/unit --ignore FileSystem::data:/tests/slow", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_provider_qualified_psdrive_preserves_pytest_nodeid_boundary(self):
target = "Data:/tests/x.py::TestA"
executions = [_bash_execution(f"pytest {target} Data:/tests/y.py --deselect FileSystem::data:/tests/x.py::TestA", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_provider_qualified_psdrive_keeps_pytest_nodeid_case_sensitive(self):
target = "Data:/tests/x.py::TestA"
executions = [_bash_execution(f"pytest {target} Data:/tests/y.py --deselect FileSystem::data:/tests/X.PY::testa", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_provider_qualified_unc_case_alias_exclusion_fails_closed(self):
target = "FileSystem:://srv/share/tests/security"
executions = [_bash_execution(f"pytest {target} FileSystem:://srv/share/tests/unit --ignore FileSystem:://srv/SHARE/tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_provider_qualified_distinct_unc_root_exclusion_fails_closed(self):
target = "FileSystem:://srv/share/tests/security"
executions = [_bash_execution(f"pytest {target} FileSystem:://srv/share/tests/unit --ignore FileSystem:://srv/other/tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_provider_qualified_unrelated_unc_exclusion_keeps_matching(self):
target = "FileSystem:://srv/share/tests/security"
executions = [_bash_execution(f"pytest {target} FileSystem:://srv/share/tests/unit --ignore FileSystem:://srv/share/tests/slow", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_provider_qualified_unc_preserves_pytest_nodeid_boundary(self):
target = "FileSystem:://srv/share/tests/x.py::TestA"
executions = [_bash_execution(f"pytest {target} FileSystem:://srv/share/tests/y.py --deselect FileSystem:://srv/SHARE/tests/x.py::TestA", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_provider_qualified_unc_keeps_pytest_nodeid_case_sensitive(self):
target = "FileSystem:://srv/share/tests/x.py::TestA"
executions = [_bash_execution(f"pytest {target} FileSystem:://srv/share/tests/y.py --deselect FileSystem:://srv/SHARE/tests/X.PY::testa", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_windows_drive_root_exclusion_overlap_is_unprovable(self):
executions = [_bash_execution("pytest D:/ D:/WS/tests/unit --ignore d:/ws/tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest D:/"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
@pytest.mark.parametrize(
("criterion_target", "ignored_target"),
(
("D:/WS/tests/./security", "d:/ws/tests/security"),
("D:/WS/tests/security", "d:/ws/tests/./security"),
("D:/WS/tests//security", "d:/ws/tests/security"),
),
)
def test_windows_lexical_alias_exclusion_is_unprovable(self, criterion_target, ignored_target):
executions = [_bash_execution(f"pytest {criterion_target} D:/WS/tests/unit --ignore {ignored_target}", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {criterion_target}"], thread_data=WINDOWS_THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_windows_relative_case_alias_exclusion_is_unprovable(self):
executions = [_bash_execution("pytest tests/security tests/unit --ignore TESTS/SECURITY", output_tail="12 passed")]
verdict = check_acceptance_criteria(
["tests_passed:pytest tests/security"],
thread_data=WINDOWS_THREAD_DATA,
bash_executions=executions,
)
assert verdict["leaves"][0]["checked"] is False
def test_windows_rooted_path_case_alias_exclusion_is_unprovable(self):
target = "/Tests/Security"
executions = [_bash_execution(f"pytest {target} /Tests/Unit --ignore /tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], thread_data=WINDOWS_THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_windows_thread_rejects_shell_ambiguous_virtual_cd_target(self):
command = "cd /mnt/user-data/workspace && pytest tests/"
executions = [_bash_execution(command, output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:{command}"], thread_data=WINDOWS_THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_unc_cd_target_uses_windows_case_rules(self):
thread_data = {
"workspace_path": "//SERVER/Share/Workspace",
"uploads_path": "//SERVER/Share/Workspace/uploads",
"outputs_path": "//SERVER/Share/Workspace/outputs",
}
command = "cd //server/share/workspace && pytest tests/"
executions = [_bash_execution(command, output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:{command}"], thread_data=thread_data, bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
@pytest.mark.parametrize("ignored", ("D:tests/security", "C:WS/tests/security"))
def test_drive_relative_exclusion_fails_closed(self, ignored):
target = "D:/WS/tests/security"
executions = [_bash_execution(f"pytest {target} D:/WS/tests/unit --ignore {ignored}", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], thread_data=WINDOWS_THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_drive_relative_case_alias_uses_windows_semantics_without_thread_context(self):
target = "D:tests/security"
executions = [_bash_execution(f"pytest {target} D:tests/unit --ignore d:TESTS/security", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_different_drive_relative_exclusion_fails_closed_without_thread_context(self):
executions = [_bash_execution("pytest C:tests/security C:tests/unit --ignore D:tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest C:tests/security"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_unrelated_exclusion_on_same_drive_relative_root_keeps_matching(self):
target = "C:tests/security"
executions = [_bash_execution(f"pytest {target} C:tests/unit --ignore c:tests/slow", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_drive_relative_and_plain_relative_exclusion_fail_closed_without_thread_context(self):
executions = [_bash_execution("pytest tests/security tests/unit --ignore D:tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/security"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_drive_relative_and_plain_relative_exclusion_fail_closed_in_windows_context(self):
executions = [_bash_execution("pytest tests/security tests/unit --ignore D:tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria(
["tests_passed:pytest tests/security"],
thread_data=WINDOWS_THREAD_DATA,
bash_executions=executions,
)
assert verdict["leaves"][0]["checked"] is False
@pytest.mark.parametrize(
("thread_data", "target", "ignored"),
(
(WINDOWS_THREAD_DATA, "tests/security", "D:/WS/tests/security"),
(WINDOWS_THREAD_DATA, "/mnt/user-data/workspace/tests/security", "D:/WS/tests/security"),
(THREAD_DATA, "tests/security", "/ws/thread/user-data/workspace/tests/security"),
),
)
def test_mixed_path_forms_in_exclusion_fail_closed(self, thread_data, target, ignored):
executions = [_bash_execution(f"pytest {target} tests/unit --ignore {ignored}", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], thread_data=thread_data, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
@pytest.mark.parametrize(
("criterion_target", "additional_target", "ignored_target"),
(
("/tests/security", "/tests/unit", "D:/tests/security"),
("D:/WS/tests/security", "D:/WS/tests/unit", "/WS/tests/security"),
("//srv/share/tests/security", "//srv/share/tests/unit", "D:/tests/security"),
),
)
def test_cross_family_absolute_exclusion_fails_closed(self, criterion_target, additional_target, ignored_target):
executions = [_bash_execution(f"pytest {criterion_target} {additional_target} --ignore {ignored_target}", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {criterion_target}"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_unc_case_alias_exclusion_is_unprovable(self):
target = "//SERVER/Share/tests/security"
executions = [_bash_execution(f"pytest {target} //SERVER/Share/tests/unit --ignore //server/share/tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
@pytest.mark.parametrize("criterion_target", ("tests/./security", "tests//security"))
def test_posix_lexical_alias_exclusion_is_unprovable(self, criterion_target):
executions = [_bash_execution(f"pytest {criterion_target} tests/unit --ignore tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {criterion_target}"], thread_data=THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_windows_trailing_dot_alias_exclusion_is_unprovable(self):
target = "D:/WS/tests/security."
executions = [_bash_execution(f"pytest {target} D:/WS/tests/unit --ignore d:/ws/tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], thread_data=WINDOWS_THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_windows_short_name_alias_exclusion_is_unprovable_without_thread_context(self):
target = "C:/longdirectoryname/tests/security"
executions = [_bash_execution(f"pytest {target} C:/longdirectoryname/tests/unit --ignore C:/LONGDI~1/tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_relative_windows_short_name_alias_exclusion_is_unprovable_in_windows_context(self):
target = "longdirectoryname/tests/security"
executions = [_bash_execution(f"pytest {target} longdirectoryname/tests/unit --ignore LONGDI~1/tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], thread_data=WINDOWS_THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_non_short_name_tilde_component_keeps_matching(self):
target = "C:/LONGDI~X/tests/security"
executions = [_bash_execution(f"pytest {target} C:/LONGDI~X/tests/unit --ignore C:/other/tests/security", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_parent_traversal_in_consumed_target_with_exclusion_is_unprovable(self):
executions = [_bash_execution("pytest tests/../security tests/unit --ignore security", output_tail="12 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/../security"], thread_data=THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["checked"] is False
def test_posix_relative_case_aliases_remain_distinct(self):
executions = [_bash_execution("pytest tests/security tests/unit --ignore TESTS/SECURITY", output_tail="12 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/security"], thread_data=THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_windows_nodeid_case_remains_distinct(self):
target = "D:/WS/tests/x.py::TestA"
executions = [_bash_execution(f"pytest {target} D:/WS/tests/y.py --deselect d:/ws/tests/X.PY::testa", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {target}"], thread_data=WINDOWS_THREAD_DATA, bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
@pytest.mark.parametrize(
("criterion_target", "ignored_target"),
(
("/opt/WS/tests/security", "/opt/ws/tests/security"),
("D:/WS/Straße/tests/security", "d:/ws/STRASSE/tests/security"),
),
)
def test_distinct_case_sensitive_exclusion_paths_do_not_overlap(self, criterion_target, ignored_target):
executions = [_bash_execution(f"pytest {criterion_target} tests/unit --ignore {ignored_target}", output_tail="12 passed")]
verdict = check_acceptance_criteria([f"tests_passed:pytest {criterion_target}"], bash_executions=executions)
assert verdict["leaves"][0]["holds"] is True
def test_unrelated_exclusion_does_not_block_the_match(self):
executions = [_bash_execution("pytest tests/security tests/unit --ignore tests/slow", output_tail="12 passed")]
verdict = check_acceptance_criteria(["tests_passed:pytest tests/security"], bash_executions=executions)