Skip to content

Commit 98c1cc2

Browse files
committed
damage-control: make the memory/wiki exemption all-or-nothing (#1148 review 3)
Exempt only ONE static path literal (optionally in expanduser/Path) that lies inside the agent-owned dir on a whole segment; any non-literal piece, or a chdir in a program naming the control plane, means no exemption. Replaces the '..' spelling denylist. Built by dotdev.dev
1 parent 084ef31 commit 98c1cc2

8 files changed

Lines changed: 229 additions & 99 deletions

File tree

‎agentwire/hooks/damage-control/bash-tool-damage-control.py‎

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def log_escape(*args, **kwargs): pass
3434

3535

3636
# === BEGIN AGENTWIRE HOOK STAMP (generated — do not edit) ===
37-
AGENTWIRE_HOOK_STAMP = {"core_sha256": "163db63e0368a8d74986285ca05667ca4c87ea6e460b5f3a566d5c322eb20539", "generated_at": "2026-10-19T13:03:51Z"}
37+
AGENTWIRE_HOOK_STAMP = {"core_sha256": "4efdfb84c3068181ab5144688ddbf6f2684795f620f1f44766e919895dfae4ac", "generated_at": "2026-10-19T13:03:51Z"}
3838
# === END AGENTWIRE HOOK STAMP ===
3939
# === BEGIN GENERATED FROM agentwire/safety/_core.py ===
4040
"""
@@ -5731,8 +5731,17 @@ def _named_control_plane_files() -> List[Tuple[str, "frozenset[str]"]]:
57315731
# Words the program text uses for a control-plane directory or file, even when the
57325732
# path itself is assembled at run time (``'damage'+'control.yml'``, ``join(h, '.claude')``).
57335733
_CONTROL_PLANE_WORDS = (".agentwire", ".claude", "damagecontrol", "settings.json")
5734-
# Directories under those names that agents write by design.
5735-
_BENIGN_CONTROL_RE = _rx(r'''\.claude/projects/[^'"\s]*?memory[^'"\s]*|\.agentwire/(?:wiki|artifacts)\b[^'"\s]*''')
5734+
# Directories under those names that agents write by design — matched on a whole path segment.
5735+
_BENIGN_PATH_RE = _rx(r'(?:^|/)\.claude/projects/[^/]+/memory(?:/|$)|(?:^|/)\.agentwire/(?:wiki|artifacts)(?:/|$)')
5736+
# The ONE expression shape the exemption covers: a single string literal, optionally
5737+
# the sole argument of ``expanduser`` / ``Path``. Anything else (a variable, a join, ``+``,
5738+
# ``os.pardir``, a call) is not a path the scan can read, so it is not exempt.
5739+
_STATIC_PATH_RES = (
5740+
_rx(r'''\s*['"]([^'"\\$@#]*)['"]\s*'''),
5741+
_rx(r'''\s*(?:os\.path\.)?expanduser\(\s*['"]([^'"\\$@#]*)['"]\s*\)\s*'''),
5742+
_rx(r'''\s*(?:pathlib\.)?Path\(\s*['"]([^'"\\$@#]*)['"]\s*\)\s*'''),
5743+
)
5744+
_CHDIR_RE = _rx(r'\bchdir\s*\(')
57365745
_CALL_RE = _rx(r'([A-Za-z_]\w*)\s*\(')
57375746
# one plain string (a double-quoted one with ``$``/``@``/``#{`` interpolates: not literal)
57385747
_LITERAL_ARG_RE = _rx(r'''\s*(?:'[^']*'|"[^"$@#\\]*")\s*''')
@@ -5851,19 +5860,23 @@ def _receiver_before(text: str, dot: int) -> str:
58515860
return text[max(pos, dot - 400):dot]
58525861

58535862

5854-
# A path that climbs out of (or is assembled from pieces of) the agent-owned directory:
5855-
# ``<memory>/../../hooks/x`` is not memory, so the exemption must not apply.
5856-
_TRAVERSAL_RE = _rx(r'\.\.|\bparents?\b|\bjoinpath\b|\bresolve\b')
5857-
5858-
58595863
def _names_control_plane(expr: str) -> bool:
5860-
"""``expr`` names a control-plane word; the agent-owned dirs are exempt unless the path can climb out of them.
5861-
5862-
A symlink INSIDE ``.claude/projects/*/memory`` that points at the control
5863-
plane is a declared residual (the text cannot see it).
5864-
"""
5865-
if not _TRAVERSAL_RE.search(expr):
5866-
expr = _BENIGN_CONTROL_RE.sub("", expr)
5864+
"""``expr`` names a control-plane word, unless it is ONE static path inside an agent-owned directory.
5865+
5866+
The exemption is all-or-nothing: only a single string literal (optionally
5867+
inside ``expanduser`` / ``Path``) whose normalized form lies inside
5868+
``.claude/projects/*/memory`` or ``.agentwire/wiki|artifacts`` and names no
5869+
other control-plane word. A non-literal piece anywhere (a variable segment,
5870+
``os.path.join``, ``+``, ``os.pardir``, a call) means the scan cannot say
5871+
where the path lands, so there is no exemption. A symlink INSIDE those
5872+
directories is a declared residual.
5873+
"""
5874+
static = next((m.group(1) for r in _STATIC_PATH_RES for m in (r.fullmatch(expr),) if m), None)
5875+
if static is not None:
5876+
norm = posixpath.normpath(static)
5877+
hit = _BENIGN_PATH_RE.search(norm)
5878+
if hit:
5879+
expr = norm[: hit.start()] + norm[hit.end():]
58675880
return any(w in expr.casefold() for w in _CONTROL_PLANE_WORDS)
58685881

58695882

@@ -5909,6 +5922,9 @@ def _program_opaque_write(program: str) -> bool:
59095922
if _INDIRECT_RE.search(program): # getattr(__import__('os'), 'system')(...) hides which sink runs
59105923
return True
59115924
in_string = _string_mask(program)
5925+
# ``os.chdir(M); os.chdir('..'); open('hooks/x', 'w')``: the working directory moves, so any
5926+
# write whose path is not an absolute literal could land anywhere.
5927+
moved = any(not in_string[m.start()] for m in _CHDIR_RE.finditer(program))
59125928
for m in _CALL_RE.finditer(program):
59135929
if in_string[m.start()]: # ``open(p, 'w')`` quoted as DATA (a fixture, a message) is not a call
59145930
continue
@@ -5949,6 +5965,8 @@ def _program_opaque_write(program: str) -> bool:
59495965
for e in exprs:
59505966
if not _LITERAL_ARG_RE.fullmatch(e) and _names_control_plane(e):
59515967
return True
5968+
if moved and not re.fullmatch(r'''\s*['"](?:/|~)[^'"]*['"]\s*''', e):
5969+
return True
59525970
return False
59535971

59545972

‎agentwire/hooks/damage-control/edit-tool-damage-control.py‎

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def log_disabled(*args, **kwargs): pass
3030

3131

3232
# === BEGIN AGENTWIRE HOOK STAMP (generated — do not edit) ===
33-
AGENTWIRE_HOOK_STAMP = {"core_sha256": "163db63e0368a8d74986285ca05667ca4c87ea6e460b5f3a566d5c322eb20539", "generated_at": "2026-10-19T13:03:51Z"}
33+
AGENTWIRE_HOOK_STAMP = {"core_sha256": "4efdfb84c3068181ab5144688ddbf6f2684795f620f1f44766e919895dfae4ac", "generated_at": "2026-10-19T13:03:51Z"}
3434
# === END AGENTWIRE HOOK STAMP ===
3535
# === BEGIN GENERATED FROM agentwire/safety/_core.py ===
3636
"""
@@ -5727,8 +5727,17 @@ def _named_control_plane_files() -> List[Tuple[str, "frozenset[str]"]]:
57275727
# Words the program text uses for a control-plane directory or file, even when the
57285728
# path itself is assembled at run time (``'damage'+'control.yml'``, ``join(h, '.claude')``).
57295729
_CONTROL_PLANE_WORDS = (".agentwire", ".claude", "damagecontrol", "settings.json")
5730-
# Directories under those names that agents write by design.
5731-
_BENIGN_CONTROL_RE = _rx(r'''\.claude/projects/[^'"\s]*?memory[^'"\s]*|\.agentwire/(?:wiki|artifacts)\b[^'"\s]*''')
5730+
# Directories under those names that agents write by design — matched on a whole path segment.
5731+
_BENIGN_PATH_RE = _rx(r'(?:^|/)\.claude/projects/[^/]+/memory(?:/|$)|(?:^|/)\.agentwire/(?:wiki|artifacts)(?:/|$)')
5732+
# The ONE expression shape the exemption covers: a single string literal, optionally
5733+
# the sole argument of ``expanduser`` / ``Path``. Anything else (a variable, a join, ``+``,
5734+
# ``os.pardir``, a call) is not a path the scan can read, so it is not exempt.
5735+
_STATIC_PATH_RES = (
5736+
_rx(r'''\s*['"]([^'"\\$@#]*)['"]\s*'''),
5737+
_rx(r'''\s*(?:os\.path\.)?expanduser\(\s*['"]([^'"\\$@#]*)['"]\s*\)\s*'''),
5738+
_rx(r'''\s*(?:pathlib\.)?Path\(\s*['"]([^'"\\$@#]*)['"]\s*\)\s*'''),
5739+
)
5740+
_CHDIR_RE = _rx(r'\bchdir\s*\(')
57325741
_CALL_RE = _rx(r'([A-Za-z_]\w*)\s*\(')
57335742
# one plain string (a double-quoted one with ``$``/``@``/``#{`` interpolates: not literal)
57345743
_LITERAL_ARG_RE = _rx(r'''\s*(?:'[^']*'|"[^"$@#\\]*")\s*''')
@@ -5847,19 +5856,23 @@ def _receiver_before(text: str, dot: int) -> str:
58475856
return text[max(pos, dot - 400):dot]
58485857

58495858

5850-
# A path that climbs out of (or is assembled from pieces of) the agent-owned directory:
5851-
# ``<memory>/../../hooks/x`` is not memory, so the exemption must not apply.
5852-
_TRAVERSAL_RE = _rx(r'\.\.|\bparents?\b|\bjoinpath\b|\bresolve\b')
5853-
5854-
58555859
def _names_control_plane(expr: str) -> bool:
5856-
"""``expr`` names a control-plane word; the agent-owned dirs are exempt unless the path can climb out of them.
5857-
5858-
A symlink INSIDE ``.claude/projects/*/memory`` that points at the control
5859-
plane is a declared residual (the text cannot see it).
5860-
"""
5861-
if not _TRAVERSAL_RE.search(expr):
5862-
expr = _BENIGN_CONTROL_RE.sub("", expr)
5860+
"""``expr`` names a control-plane word, unless it is ONE static path inside an agent-owned directory.
5861+
5862+
The exemption is all-or-nothing: only a single string literal (optionally
5863+
inside ``expanduser`` / ``Path``) whose normalized form lies inside
5864+
``.claude/projects/*/memory`` or ``.agentwire/wiki|artifacts`` and names no
5865+
other control-plane word. A non-literal piece anywhere (a variable segment,
5866+
``os.path.join``, ``+``, ``os.pardir``, a call) means the scan cannot say
5867+
where the path lands, so there is no exemption. A symlink INSIDE those
5868+
directories is a declared residual.
5869+
"""
5870+
static = next((m.group(1) for r in _STATIC_PATH_RES for m in (r.fullmatch(expr),) if m), None)
5871+
if static is not None:
5872+
norm = posixpath.normpath(static)
5873+
hit = _BENIGN_PATH_RE.search(norm)
5874+
if hit:
5875+
expr = norm[: hit.start()] + norm[hit.end():]
58635876
return any(w in expr.casefold() for w in _CONTROL_PLANE_WORDS)
58645877

58655878

@@ -5905,6 +5918,9 @@ def _program_opaque_write(program: str) -> bool:
59055918
if _INDIRECT_RE.search(program): # getattr(__import__('os'), 'system')(...) hides which sink runs
59065919
return True
59075920
in_string = _string_mask(program)
5921+
# ``os.chdir(M); os.chdir('..'); open('hooks/x', 'w')``: the working directory moves, so any
5922+
# write whose path is not an absolute literal could land anywhere.
5923+
moved = any(not in_string[m.start()] for m in _CHDIR_RE.finditer(program))
59085924
for m in _CALL_RE.finditer(program):
59095925
if in_string[m.start()]: # ``open(p, 'w')`` quoted as DATA (a fixture, a message) is not a call
59105926
continue
@@ -5945,6 +5961,8 @@ def _program_opaque_write(program: str) -> bool:
59455961
for e in exprs:
59465962
if not _LITERAL_ARG_RE.fullmatch(e) and _names_control_plane(e):
59475963
return True
5964+
if moved and not re.fullmatch(r'''\s*['"](?:/|~)[^'"]*['"]\s*''', e):
5965+
return True
59485966
return False
59495967

59505968

‎agentwire/hooks/damage-control/mcp-tool-damage-control.py‎

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def log_escape(*args, **kwargs): pass
4949

5050

5151
# === BEGIN AGENTWIRE HOOK STAMP (generated — do not edit) ===
52-
AGENTWIRE_HOOK_STAMP = {"core_sha256": "163db63e0368a8d74986285ca05667ca4c87ea6e460b5f3a566d5c322eb20539", "generated_at": "2026-10-19T13:03:51Z"}
52+
AGENTWIRE_HOOK_STAMP = {"core_sha256": "4efdfb84c3068181ab5144688ddbf6f2684795f620f1f44766e919895dfae4ac", "generated_at": "2026-10-19T13:03:51Z"}
5353
# === END AGENTWIRE HOOK STAMP ===
5454
# === BEGIN GENERATED FROM agentwire/safety/_core.py ===
5555
"""
@@ -5746,8 +5746,17 @@ def _named_control_plane_files() -> List[Tuple[str, "frozenset[str]"]]:
57465746
# Words the program text uses for a control-plane directory or file, even when the
57475747
# path itself is assembled at run time (``'damage'+'control.yml'``, ``join(h, '.claude')``).
57485748
_CONTROL_PLANE_WORDS = (".agentwire", ".claude", "damagecontrol", "settings.json")
5749-
# Directories under those names that agents write by design.
5750-
_BENIGN_CONTROL_RE = _rx(r'''\.claude/projects/[^'"\s]*?memory[^'"\s]*|\.agentwire/(?:wiki|artifacts)\b[^'"\s]*''')
5749+
# Directories under those names that agents write by design — matched on a whole path segment.
5750+
_BENIGN_PATH_RE = _rx(r'(?:^|/)\.claude/projects/[^/]+/memory(?:/|$)|(?:^|/)\.agentwire/(?:wiki|artifacts)(?:/|$)')
5751+
# The ONE expression shape the exemption covers: a single string literal, optionally
5752+
# the sole argument of ``expanduser`` / ``Path``. Anything else (a variable, a join, ``+``,
5753+
# ``os.pardir``, a call) is not a path the scan can read, so it is not exempt.
5754+
_STATIC_PATH_RES = (
5755+
_rx(r'''\s*['"]([^'"\\$@#]*)['"]\s*'''),
5756+
_rx(r'''\s*(?:os\.path\.)?expanduser\(\s*['"]([^'"\\$@#]*)['"]\s*\)\s*'''),
5757+
_rx(r'''\s*(?:pathlib\.)?Path\(\s*['"]([^'"\\$@#]*)['"]\s*\)\s*'''),
5758+
)
5759+
_CHDIR_RE = _rx(r'\bchdir\s*\(')
57515760
_CALL_RE = _rx(r'([A-Za-z_]\w*)\s*\(')
57525761
# one plain string (a double-quoted one with ``$``/``@``/``#{`` interpolates: not literal)
57535762
_LITERAL_ARG_RE = _rx(r'''\s*(?:'[^']*'|"[^"$@#\\]*")\s*''')
@@ -5866,19 +5875,23 @@ def _receiver_before(text: str, dot: int) -> str:
58665875
return text[max(pos, dot - 400):dot]
58675876

58685877

5869-
# A path that climbs out of (or is assembled from pieces of) the agent-owned directory:
5870-
# ``<memory>/../../hooks/x`` is not memory, so the exemption must not apply.
5871-
_TRAVERSAL_RE = _rx(r'\.\.|\bparents?\b|\bjoinpath\b|\bresolve\b')
5872-
5873-
58745878
def _names_control_plane(expr: str) -> bool:
5875-
"""``expr`` names a control-plane word; the agent-owned dirs are exempt unless the path can climb out of them.
5876-
5877-
A symlink INSIDE ``.claude/projects/*/memory`` that points at the control
5878-
plane is a declared residual (the text cannot see it).
5879-
"""
5880-
if not _TRAVERSAL_RE.search(expr):
5881-
expr = _BENIGN_CONTROL_RE.sub("", expr)
5879+
"""``expr`` names a control-plane word, unless it is ONE static path inside an agent-owned directory.
5880+
5881+
The exemption is all-or-nothing: only a single string literal (optionally
5882+
inside ``expanduser`` / ``Path``) whose normalized form lies inside
5883+
``.claude/projects/*/memory`` or ``.agentwire/wiki|artifacts`` and names no
5884+
other control-plane word. A non-literal piece anywhere (a variable segment,
5885+
``os.path.join``, ``+``, ``os.pardir``, a call) means the scan cannot say
5886+
where the path lands, so there is no exemption. A symlink INSIDE those
5887+
directories is a declared residual.
5888+
"""
5889+
static = next((m.group(1) for r in _STATIC_PATH_RES for m in (r.fullmatch(expr),) if m), None)
5890+
if static is not None:
5891+
norm = posixpath.normpath(static)
5892+
hit = _BENIGN_PATH_RE.search(norm)
5893+
if hit:
5894+
expr = norm[: hit.start()] + norm[hit.end():]
58825895
return any(w in expr.casefold() for w in _CONTROL_PLANE_WORDS)
58835896

58845897

@@ -5924,6 +5937,9 @@ def _program_opaque_write(program: str) -> bool:
59245937
if _INDIRECT_RE.search(program): # getattr(__import__('os'), 'system')(...) hides which sink runs
59255938
return True
59265939
in_string = _string_mask(program)
5940+
# ``os.chdir(M); os.chdir('..'); open('hooks/x', 'w')``: the working directory moves, so any
5941+
# write whose path is not an absolute literal could land anywhere.
5942+
moved = any(not in_string[m.start()] for m in _CHDIR_RE.finditer(program))
59275943
for m in _CALL_RE.finditer(program):
59285944
if in_string[m.start()]: # ``open(p, 'w')`` quoted as DATA (a fixture, a message) is not a call
59295945
continue
@@ -5964,6 +5980,8 @@ def _program_opaque_write(program: str) -> bool:
59645980
for e in exprs:
59655981
if not _LITERAL_ARG_RE.fullmatch(e) and _names_control_plane(e):
59665982
return True
5983+
if moved and not re.fullmatch(r'''\s*['"](?:/|~)[^'"]*['"]\s*''', e):
5984+
return True
59675985
return False
59685986

59695987

‎agentwire/hooks/damage-control/read-tool-damage-control.py‎

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def log_disabled(*args, **kwargs): pass
3636

3737

3838
# === BEGIN AGENTWIRE HOOK STAMP (generated — do not edit) ===
39-
AGENTWIRE_HOOK_STAMP = {"core_sha256": "163db63e0368a8d74986285ca05667ca4c87ea6e460b5f3a566d5c322eb20539", "generated_at": "2026-10-19T13:03:51Z"}
39+
AGENTWIRE_HOOK_STAMP = {"core_sha256": "4efdfb84c3068181ab5144688ddbf6f2684795f620f1f44766e919895dfae4ac", "generated_at": "2026-10-19T13:03:51Z"}
4040
# === END AGENTWIRE HOOK STAMP ===
4141
# === BEGIN GENERATED FROM agentwire/safety/_core.py ===
4242
"""
@@ -5733,8 +5733,17 @@ def _named_control_plane_files() -> List[Tuple[str, "frozenset[str]"]]:
57335733
# Words the program text uses for a control-plane directory or file, even when the
57345734
# path itself is assembled at run time (``'damage'+'control.yml'``, ``join(h, '.claude')``).
57355735
_CONTROL_PLANE_WORDS = (".agentwire", ".claude", "damagecontrol", "settings.json")
5736-
# Directories under those names that agents write by design.
5737-
_BENIGN_CONTROL_RE = _rx(r'''\.claude/projects/[^'"\s]*?memory[^'"\s]*|\.agentwire/(?:wiki|artifacts)\b[^'"\s]*''')
5736+
# Directories under those names that agents write by design — matched on a whole path segment.
5737+
_BENIGN_PATH_RE = _rx(r'(?:^|/)\.claude/projects/[^/]+/memory(?:/|$)|(?:^|/)\.agentwire/(?:wiki|artifacts)(?:/|$)')
5738+
# The ONE expression shape the exemption covers: a single string literal, optionally
5739+
# the sole argument of ``expanduser`` / ``Path``. Anything else (a variable, a join, ``+``,
5740+
# ``os.pardir``, a call) is not a path the scan can read, so it is not exempt.
5741+
_STATIC_PATH_RES = (
5742+
_rx(r'''\s*['"]([^'"\\$@#]*)['"]\s*'''),
5743+
_rx(r'''\s*(?:os\.path\.)?expanduser\(\s*['"]([^'"\\$@#]*)['"]\s*\)\s*'''),
5744+
_rx(r'''\s*(?:pathlib\.)?Path\(\s*['"]([^'"\\$@#]*)['"]\s*\)\s*'''),
5745+
)
5746+
_CHDIR_RE = _rx(r'\bchdir\s*\(')
57385747
_CALL_RE = _rx(r'([A-Za-z_]\w*)\s*\(')
57395748
# one plain string (a double-quoted one with ``$``/``@``/``#{`` interpolates: not literal)
57405749
_LITERAL_ARG_RE = _rx(r'''\s*(?:'[^']*'|"[^"$@#\\]*")\s*''')
@@ -5853,19 +5862,23 @@ def _receiver_before(text: str, dot: int) -> str:
58535862
return text[max(pos, dot - 400):dot]
58545863

58555864

5856-
# A path that climbs out of (or is assembled from pieces of) the agent-owned directory:
5857-
# ``<memory>/../../hooks/x`` is not memory, so the exemption must not apply.
5858-
_TRAVERSAL_RE = _rx(r'\.\.|\bparents?\b|\bjoinpath\b|\bresolve\b')
5859-
5860-
58615865
def _names_control_plane(expr: str) -> bool:
5862-
"""``expr`` names a control-plane word; the agent-owned dirs are exempt unless the path can climb out of them.
5863-
5864-
A symlink INSIDE ``.claude/projects/*/memory`` that points at the control
5865-
plane is a declared residual (the text cannot see it).
5866-
"""
5867-
if not _TRAVERSAL_RE.search(expr):
5868-
expr = _BENIGN_CONTROL_RE.sub("", expr)
5866+
"""``expr`` names a control-plane word, unless it is ONE static path inside an agent-owned directory.
5867+
5868+
The exemption is all-or-nothing: only a single string literal (optionally
5869+
inside ``expanduser`` / ``Path``) whose normalized form lies inside
5870+
``.claude/projects/*/memory`` or ``.agentwire/wiki|artifacts`` and names no
5871+
other control-plane word. A non-literal piece anywhere (a variable segment,
5872+
``os.path.join``, ``+``, ``os.pardir``, a call) means the scan cannot say
5873+
where the path lands, so there is no exemption. A symlink INSIDE those
5874+
directories is a declared residual.
5875+
"""
5876+
static = next((m.group(1) for r in _STATIC_PATH_RES for m in (r.fullmatch(expr),) if m), None)
5877+
if static is not None:
5878+
norm = posixpath.normpath(static)
5879+
hit = _BENIGN_PATH_RE.search(norm)
5880+
if hit:
5881+
expr = norm[: hit.start()] + norm[hit.end():]
58695882
return any(w in expr.casefold() for w in _CONTROL_PLANE_WORDS)
58705883

58715884

@@ -5911,6 +5924,9 @@ def _program_opaque_write(program: str) -> bool:
59115924
if _INDIRECT_RE.search(program): # getattr(__import__('os'), 'system')(...) hides which sink runs
59125925
return True
59135926
in_string = _string_mask(program)
5927+
# ``os.chdir(M); os.chdir('..'); open('hooks/x', 'w')``: the working directory moves, so any
5928+
# write whose path is not an absolute literal could land anywhere.
5929+
moved = any(not in_string[m.start()] for m in _CHDIR_RE.finditer(program))
59145930
for m in _CALL_RE.finditer(program):
59155931
if in_string[m.start()]: # ``open(p, 'w')`` quoted as DATA (a fixture, a message) is not a call
59165932
continue
@@ -5951,6 +5967,8 @@ def _program_opaque_write(program: str) -> bool:
59515967
for e in exprs:
59525968
if not _LITERAL_ARG_RE.fullmatch(e) and _names_control_plane(e):
59535969
return True
5970+
if moved and not re.fullmatch(r'''\s*['"](?:/|~)[^'"]*['"]\s*''', e):
5971+
return True
59545972
return False
59555973

59565974

0 commit comments

Comments
 (0)