Skip to content

Commit 1447a59

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 e0a8fb0 commit 1447a59

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": "4b7c098a846a22010ae120e05cbbb99931f66a3b2c5473608f8d51d07a332553", "generated_at": "2026-10-19T12:39:49Z"}
37+
AGENTWIRE_HOOK_STAMP = {"core_sha256": "5dca5b9508b51769a93037ce86a9912154ddcd8dd49d26a8e800e054708c413b", "generated_at": "2026-10-19T12:48:45Z"}
3838
# === END AGENTWIRE HOOK STAMP ===
3939
# === BEGIN GENERATED FROM agentwire/safety/_core.py ===
4040
"""
@@ -5099,8 +5099,17 @@ def _named_control_plane_files() -> List[Tuple[str, "frozenset[str]"]]:
50995099
# Words the program text uses for a control-plane directory or file, even when the
51005100
# path itself is assembled at run time (``'damage'+'control.yml'``, ``join(h, '.claude')``).
51015101
_CONTROL_PLANE_WORDS = (".agentwire", ".claude", "damagecontrol", "settings.json")
5102-
# Directories under those names that agents write by design.
5103-
_BENIGN_CONTROL_RE = _rx(r'''\.claude/projects/[^'"\s]*?memory[^'"\s]*|\.agentwire/(?:wiki|artifacts)\b[^'"\s]*''')
5102+
# Directories under those names that agents write by design — matched on a whole path segment.
5103+
_BENIGN_PATH_RE = _rx(r'(?:^|/)\.claude/projects/[^/]+/memory(?:/|$)|(?:^|/)\.agentwire/(?:wiki|artifacts)(?:/|$)')
5104+
# The ONE expression shape the exemption covers: a single string literal, optionally
5105+
# the sole argument of ``expanduser`` / ``Path``. Anything else (a variable, a join, ``+``,
5106+
# ``os.pardir``, a call) is not a path the scan can read, so it is not exempt.
5107+
_STATIC_PATH_RES = (
5108+
_rx(r'''\s*['"]([^'"\\$@#]*)['"]\s*'''),
5109+
_rx(r'''\s*(?:os\.path\.)?expanduser\(\s*['"]([^'"\\$@#]*)['"]\s*\)\s*'''),
5110+
_rx(r'''\s*(?:pathlib\.)?Path\(\s*['"]([^'"\\$@#]*)['"]\s*\)\s*'''),
5111+
)
5112+
_CHDIR_RE = _rx(r'\bchdir\s*\(')
51045113
_CALL_RE = _rx(r'([A-Za-z_]\w*)\s*\(')
51055114
# one plain string (a double-quoted one with ``$``/``@``/``#{`` interpolates: not literal)
51065115
_LITERAL_ARG_RE = _rx(r'''\s*(?:'[^']*'|"[^"$@#\\]*")\s*''')
@@ -5219,19 +5228,23 @@ def _receiver_before(text: str, dot: int) -> str:
52195228
return text[max(pos, dot - 400):dot]
52205229

52215230

5222-
# A path that climbs out of (or is assembled from pieces of) the agent-owned directory:
5223-
# ``<memory>/../../hooks/x`` is not memory, so the exemption must not apply.
5224-
_TRAVERSAL_RE = _rx(r'\.\.|\bparents?\b|\bjoinpath\b|\bresolve\b')
5225-
5226-
52275231
def _names_control_plane(expr: str) -> bool:
5228-
"""``expr`` names a control-plane word; the agent-owned dirs are exempt unless the path can climb out of them.
5229-
5230-
A symlink INSIDE ``.claude/projects/*/memory`` that points at the control
5231-
plane is a declared residual (the text cannot see it).
5232-
"""
5233-
if not _TRAVERSAL_RE.search(expr):
5234-
expr = _BENIGN_CONTROL_RE.sub("", expr)
5232+
"""``expr`` names a control-plane word, unless it is ONE static path inside an agent-owned directory.
5233+
5234+
The exemption is all-or-nothing: only a single string literal (optionally
5235+
inside ``expanduser`` / ``Path``) whose normalized form lies inside
5236+
``.claude/projects/*/memory`` or ``.agentwire/wiki|artifacts`` and names no
5237+
other control-plane word. A non-literal piece anywhere (a variable segment,
5238+
``os.path.join``, ``+``, ``os.pardir``, a call) means the scan cannot say
5239+
where the path lands, so there is no exemption. A symlink INSIDE those
5240+
directories is a declared residual.
5241+
"""
5242+
static = next((m.group(1) for r in _STATIC_PATH_RES for m in (r.fullmatch(expr),) if m), None)
5243+
if static is not None:
5244+
norm = posixpath.normpath(static)
5245+
hit = _BENIGN_PATH_RE.search(norm)
5246+
if hit:
5247+
expr = norm[: hit.start()] + norm[hit.end():]
52355248
return any(w in expr.casefold() for w in _CONTROL_PLANE_WORDS)
52365249

52375250

@@ -5277,6 +5290,9 @@ def _program_opaque_write(program: str) -> bool:
52775290
if _INDIRECT_RE.search(program): # getattr(__import__('os'), 'system')(...) hides which sink runs
52785291
return True
52795292
in_string = _string_mask(program)
5293+
# ``os.chdir(M); os.chdir('..'); open('hooks/x', 'w')``: the working directory moves, so any
5294+
# write whose path is not an absolute literal could land anywhere.
5295+
moved = any(not in_string[m.start()] for m in _CHDIR_RE.finditer(program))
52805296
for m in _CALL_RE.finditer(program):
52815297
if in_string[m.start()]: # ``open(p, 'w')`` quoted as DATA (a fixture, a message) is not a call
52825298
continue
@@ -5317,6 +5333,8 @@ def _program_opaque_write(program: str) -> bool:
53175333
for e in exprs:
53185334
if not _LITERAL_ARG_RE.fullmatch(e) and _names_control_plane(e):
53195335
return True
5336+
if moved and not re.fullmatch(r'''\s*['"](?:/|~)[^'"]*['"]\s*''', e):
5337+
return True
53205338
return False
53215339

53225340

‎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": "4b7c098a846a22010ae120e05cbbb99931f66a3b2c5473608f8d51d07a332553", "generated_at": "2026-10-19T12:39:49Z"}
33+
AGENTWIRE_HOOK_STAMP = {"core_sha256": "5dca5b9508b51769a93037ce86a9912154ddcd8dd49d26a8e800e054708c413b", "generated_at": "2026-10-19T12:48:45Z"}
3434
# === END AGENTWIRE HOOK STAMP ===
3535
# === BEGIN GENERATED FROM agentwire/safety/_core.py ===
3636
"""
@@ -5095,8 +5095,17 @@ def _named_control_plane_files() -> List[Tuple[str, "frozenset[str]"]]:
50955095
# Words the program text uses for a control-plane directory or file, even when the
50965096
# path itself is assembled at run time (``'damage'+'control.yml'``, ``join(h, '.claude')``).
50975097
_CONTROL_PLANE_WORDS = (".agentwire", ".claude", "damagecontrol", "settings.json")
5098-
# Directories under those names that agents write by design.
5099-
_BENIGN_CONTROL_RE = _rx(r'''\.claude/projects/[^'"\s]*?memory[^'"\s]*|\.agentwire/(?:wiki|artifacts)\b[^'"\s]*''')
5098+
# Directories under those names that agents write by design — matched on a whole path segment.
5099+
_BENIGN_PATH_RE = _rx(r'(?:^|/)\.claude/projects/[^/]+/memory(?:/|$)|(?:^|/)\.agentwire/(?:wiki|artifacts)(?:/|$)')
5100+
# The ONE expression shape the exemption covers: a single string literal, optionally
5101+
# the sole argument of ``expanduser`` / ``Path``. Anything else (a variable, a join, ``+``,
5102+
# ``os.pardir``, a call) is not a path the scan can read, so it is not exempt.
5103+
_STATIC_PATH_RES = (
5104+
_rx(r'''\s*['"]([^'"\\$@#]*)['"]\s*'''),
5105+
_rx(r'''\s*(?:os\.path\.)?expanduser\(\s*['"]([^'"\\$@#]*)['"]\s*\)\s*'''),
5106+
_rx(r'''\s*(?:pathlib\.)?Path\(\s*['"]([^'"\\$@#]*)['"]\s*\)\s*'''),
5107+
)
5108+
_CHDIR_RE = _rx(r'\bchdir\s*\(')
51005109
_CALL_RE = _rx(r'([A-Za-z_]\w*)\s*\(')
51015110
# one plain string (a double-quoted one with ``$``/``@``/``#{`` interpolates: not literal)
51025111
_LITERAL_ARG_RE = _rx(r'''\s*(?:'[^']*'|"[^"$@#\\]*")\s*''')
@@ -5215,19 +5224,23 @@ def _receiver_before(text: str, dot: int) -> str:
52155224
return text[max(pos, dot - 400):dot]
52165225

52175226

5218-
# A path that climbs out of (or is assembled from pieces of) the agent-owned directory:
5219-
# ``<memory>/../../hooks/x`` is not memory, so the exemption must not apply.
5220-
_TRAVERSAL_RE = _rx(r'\.\.|\bparents?\b|\bjoinpath\b|\bresolve\b')
5221-
5222-
52235227
def _names_control_plane(expr: str) -> bool:
5224-
"""``expr`` names a control-plane word; the agent-owned dirs are exempt unless the path can climb out of them.
5225-
5226-
A symlink INSIDE ``.claude/projects/*/memory`` that points at the control
5227-
plane is a declared residual (the text cannot see it).
5228-
"""
5229-
if not _TRAVERSAL_RE.search(expr):
5230-
expr = _BENIGN_CONTROL_RE.sub("", expr)
5228+
"""``expr`` names a control-plane word, unless it is ONE static path inside an agent-owned directory.
5229+
5230+
The exemption is all-or-nothing: only a single string literal (optionally
5231+
inside ``expanduser`` / ``Path``) whose normalized form lies inside
5232+
``.claude/projects/*/memory`` or ``.agentwire/wiki|artifacts`` and names no
5233+
other control-plane word. A non-literal piece anywhere (a variable segment,
5234+
``os.path.join``, ``+``, ``os.pardir``, a call) means the scan cannot say
5235+
where the path lands, so there is no exemption. A symlink INSIDE those
5236+
directories is a declared residual.
5237+
"""
5238+
static = next((m.group(1) for r in _STATIC_PATH_RES for m in (r.fullmatch(expr),) if m), None)
5239+
if static is not None:
5240+
norm = posixpath.normpath(static)
5241+
hit = _BENIGN_PATH_RE.search(norm)
5242+
if hit:
5243+
expr = norm[: hit.start()] + norm[hit.end():]
52315244
return any(w in expr.casefold() for w in _CONTROL_PLANE_WORDS)
52325245

52335246

@@ -5273,6 +5286,9 @@ def _program_opaque_write(program: str) -> bool:
52735286
if _INDIRECT_RE.search(program): # getattr(__import__('os'), 'system')(...) hides which sink runs
52745287
return True
52755288
in_string = _string_mask(program)
5289+
# ``os.chdir(M); os.chdir('..'); open('hooks/x', 'w')``: the working directory moves, so any
5290+
# write whose path is not an absolute literal could land anywhere.
5291+
moved = any(not in_string[m.start()] for m in _CHDIR_RE.finditer(program))
52765292
for m in _CALL_RE.finditer(program):
52775293
if in_string[m.start()]: # ``open(p, 'w')`` quoted as DATA (a fixture, a message) is not a call
52785294
continue
@@ -5313,6 +5329,8 @@ def _program_opaque_write(program: str) -> bool:
53135329
for e in exprs:
53145330
if not _LITERAL_ARG_RE.fullmatch(e) and _names_control_plane(e):
53155331
return True
5332+
if moved and not re.fullmatch(r'''\s*['"](?:/|~)[^'"]*['"]\s*''', e):
5333+
return True
53165334
return False
53175335

53185336

‎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": "4b7c098a846a22010ae120e05cbbb99931f66a3b2c5473608f8d51d07a332553", "generated_at": "2026-10-19T12:39:49Z"}
52+
AGENTWIRE_HOOK_STAMP = {"core_sha256": "5dca5b9508b51769a93037ce86a9912154ddcd8dd49d26a8e800e054708c413b", "generated_at": "2026-10-19T12:48:45Z"}
5353
# === END AGENTWIRE HOOK STAMP ===
5454
# === BEGIN GENERATED FROM agentwire/safety/_core.py ===
5555
"""
@@ -5114,8 +5114,17 @@ def _named_control_plane_files() -> List[Tuple[str, "frozenset[str]"]]:
51145114
# Words the program text uses for a control-plane directory or file, even when the
51155115
# path itself is assembled at run time (``'damage'+'control.yml'``, ``join(h, '.claude')``).
51165116
_CONTROL_PLANE_WORDS = (".agentwire", ".claude", "damagecontrol", "settings.json")
5117-
# Directories under those names that agents write by design.
5118-
_BENIGN_CONTROL_RE = _rx(r'''\.claude/projects/[^'"\s]*?memory[^'"\s]*|\.agentwire/(?:wiki|artifacts)\b[^'"\s]*''')
5117+
# Directories under those names that agents write by design — matched on a whole path segment.
5118+
_BENIGN_PATH_RE = _rx(r'(?:^|/)\.claude/projects/[^/]+/memory(?:/|$)|(?:^|/)\.agentwire/(?:wiki|artifacts)(?:/|$)')
5119+
# The ONE expression shape the exemption covers: a single string literal, optionally
5120+
# the sole argument of ``expanduser`` / ``Path``. Anything else (a variable, a join, ``+``,
5121+
# ``os.pardir``, a call) is not a path the scan can read, so it is not exempt.
5122+
_STATIC_PATH_RES = (
5123+
_rx(r'''\s*['"]([^'"\\$@#]*)['"]\s*'''),
5124+
_rx(r'''\s*(?:os\.path\.)?expanduser\(\s*['"]([^'"\\$@#]*)['"]\s*\)\s*'''),
5125+
_rx(r'''\s*(?:pathlib\.)?Path\(\s*['"]([^'"\\$@#]*)['"]\s*\)\s*'''),
5126+
)
5127+
_CHDIR_RE = _rx(r'\bchdir\s*\(')
51195128
_CALL_RE = _rx(r'([A-Za-z_]\w*)\s*\(')
51205129
# one plain string (a double-quoted one with ``$``/``@``/``#{`` interpolates: not literal)
51215130
_LITERAL_ARG_RE = _rx(r'''\s*(?:'[^']*'|"[^"$@#\\]*")\s*''')
@@ -5234,19 +5243,23 @@ def _receiver_before(text: str, dot: int) -> str:
52345243
return text[max(pos, dot - 400):dot]
52355244

52365245

5237-
# A path that climbs out of (or is assembled from pieces of) the agent-owned directory:
5238-
# ``<memory>/../../hooks/x`` is not memory, so the exemption must not apply.
5239-
_TRAVERSAL_RE = _rx(r'\.\.|\bparents?\b|\bjoinpath\b|\bresolve\b')
5240-
5241-
52425246
def _names_control_plane(expr: str) -> bool:
5243-
"""``expr`` names a control-plane word; the agent-owned dirs are exempt unless the path can climb out of them.
5244-
5245-
A symlink INSIDE ``.claude/projects/*/memory`` that points at the control
5246-
plane is a declared residual (the text cannot see it).
5247-
"""
5248-
if not _TRAVERSAL_RE.search(expr):
5249-
expr = _BENIGN_CONTROL_RE.sub("", expr)
5247+
"""``expr`` names a control-plane word, unless it is ONE static path inside an agent-owned directory.
5248+
5249+
The exemption is all-or-nothing: only a single string literal (optionally
5250+
inside ``expanduser`` / ``Path``) whose normalized form lies inside
5251+
``.claude/projects/*/memory`` or ``.agentwire/wiki|artifacts`` and names no
5252+
other control-plane word. A non-literal piece anywhere (a variable segment,
5253+
``os.path.join``, ``+``, ``os.pardir``, a call) means the scan cannot say
5254+
where the path lands, so there is no exemption. A symlink INSIDE those
5255+
directories is a declared residual.
5256+
"""
5257+
static = next((m.group(1) for r in _STATIC_PATH_RES for m in (r.fullmatch(expr),) if m), None)
5258+
if static is not None:
5259+
norm = posixpath.normpath(static)
5260+
hit = _BENIGN_PATH_RE.search(norm)
5261+
if hit:
5262+
expr = norm[: hit.start()] + norm[hit.end():]
52505263
return any(w in expr.casefold() for w in _CONTROL_PLANE_WORDS)
52515264

52525265

@@ -5292,6 +5305,9 @@ def _program_opaque_write(program: str) -> bool:
52925305
if _INDIRECT_RE.search(program): # getattr(__import__('os'), 'system')(...) hides which sink runs
52935306
return True
52945307
in_string = _string_mask(program)
5308+
# ``os.chdir(M); os.chdir('..'); open('hooks/x', 'w')``: the working directory moves, so any
5309+
# write whose path is not an absolute literal could land anywhere.
5310+
moved = any(not in_string[m.start()] for m in _CHDIR_RE.finditer(program))
52955311
for m in _CALL_RE.finditer(program):
52965312
if in_string[m.start()]: # ``open(p, 'w')`` quoted as DATA (a fixture, a message) is not a call
52975313
continue
@@ -5332,6 +5348,8 @@ def _program_opaque_write(program: str) -> bool:
53325348
for e in exprs:
53335349
if not _LITERAL_ARG_RE.fullmatch(e) and _names_control_plane(e):
53345350
return True
5351+
if moved and not re.fullmatch(r'''\s*['"](?:/|~)[^'"]*['"]\s*''', e):
5352+
return True
53355353
return False
53365354

53375355

‎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": "4b7c098a846a22010ae120e05cbbb99931f66a3b2c5473608f8d51d07a332553", "generated_at": "2026-10-19T12:39:49Z"}
39+
AGENTWIRE_HOOK_STAMP = {"core_sha256": "5dca5b9508b51769a93037ce86a9912154ddcd8dd49d26a8e800e054708c413b", "generated_at": "2026-10-19T12:48:45Z"}
4040
# === END AGENTWIRE HOOK STAMP ===
4141
# === BEGIN GENERATED FROM agentwire/safety/_core.py ===
4242
"""
@@ -5101,8 +5101,17 @@ def _named_control_plane_files() -> List[Tuple[str, "frozenset[str]"]]:
51015101
# Words the program text uses for a control-plane directory or file, even when the
51025102
# path itself is assembled at run time (``'damage'+'control.yml'``, ``join(h, '.claude')``).
51035103
_CONTROL_PLANE_WORDS = (".agentwire", ".claude", "damagecontrol", "settings.json")
5104-
# Directories under those names that agents write by design.
5105-
_BENIGN_CONTROL_RE = _rx(r'''\.claude/projects/[^'"\s]*?memory[^'"\s]*|\.agentwire/(?:wiki|artifacts)\b[^'"\s]*''')
5104+
# Directories under those names that agents write by design — matched on a whole path segment.
5105+
_BENIGN_PATH_RE = _rx(r'(?:^|/)\.claude/projects/[^/]+/memory(?:/|$)|(?:^|/)\.agentwire/(?:wiki|artifacts)(?:/|$)')
5106+
# The ONE expression shape the exemption covers: a single string literal, optionally
5107+
# the sole argument of ``expanduser`` / ``Path``. Anything else (a variable, a join, ``+``,
5108+
# ``os.pardir``, a call) is not a path the scan can read, so it is not exempt.
5109+
_STATIC_PATH_RES = (
5110+
_rx(r'''\s*['"]([^'"\\$@#]*)['"]\s*'''),
5111+
_rx(r'''\s*(?:os\.path\.)?expanduser\(\s*['"]([^'"\\$@#]*)['"]\s*\)\s*'''),
5112+
_rx(r'''\s*(?:pathlib\.)?Path\(\s*['"]([^'"\\$@#]*)['"]\s*\)\s*'''),
5113+
)
5114+
_CHDIR_RE = _rx(r'\bchdir\s*\(')
51065115
_CALL_RE = _rx(r'([A-Za-z_]\w*)\s*\(')
51075116
# one plain string (a double-quoted one with ``$``/``@``/``#{`` interpolates: not literal)
51085117
_LITERAL_ARG_RE = _rx(r'''\s*(?:'[^']*'|"[^"$@#\\]*")\s*''')
@@ -5221,19 +5230,23 @@ def _receiver_before(text: str, dot: int) -> str:
52215230
return text[max(pos, dot - 400):dot]
52225231

52235232

5224-
# A path that climbs out of (or is assembled from pieces of) the agent-owned directory:
5225-
# ``<memory>/../../hooks/x`` is not memory, so the exemption must not apply.
5226-
_TRAVERSAL_RE = _rx(r'\.\.|\bparents?\b|\bjoinpath\b|\bresolve\b')
5227-
5228-
52295233
def _names_control_plane(expr: str) -> bool:
5230-
"""``expr`` names a control-plane word; the agent-owned dirs are exempt unless the path can climb out of them.
5231-
5232-
A symlink INSIDE ``.claude/projects/*/memory`` that points at the control
5233-
plane is a declared residual (the text cannot see it).
5234-
"""
5235-
if not _TRAVERSAL_RE.search(expr):
5236-
expr = _BENIGN_CONTROL_RE.sub("", expr)
5234+
"""``expr`` names a control-plane word, unless it is ONE static path inside an agent-owned directory.
5235+
5236+
The exemption is all-or-nothing: only a single string literal (optionally
5237+
inside ``expanduser`` / ``Path``) whose normalized form lies inside
5238+
``.claude/projects/*/memory`` or ``.agentwire/wiki|artifacts`` and names no
5239+
other control-plane word. A non-literal piece anywhere (a variable segment,
5240+
``os.path.join``, ``+``, ``os.pardir``, a call) means the scan cannot say
5241+
where the path lands, so there is no exemption. A symlink INSIDE those
5242+
directories is a declared residual.
5243+
"""
5244+
static = next((m.group(1) for r in _STATIC_PATH_RES for m in (r.fullmatch(expr),) if m), None)
5245+
if static is not None:
5246+
norm = posixpath.normpath(static)
5247+
hit = _BENIGN_PATH_RE.search(norm)
5248+
if hit:
5249+
expr = norm[: hit.start()] + norm[hit.end():]
52375250
return any(w in expr.casefold() for w in _CONTROL_PLANE_WORDS)
52385251

52395252

@@ -5279,6 +5292,9 @@ def _program_opaque_write(program: str) -> bool:
52795292
if _INDIRECT_RE.search(program): # getattr(__import__('os'), 'system')(...) hides which sink runs
52805293
return True
52815294
in_string = _string_mask(program)
5295+
# ``os.chdir(M); os.chdir('..'); open('hooks/x', 'w')``: the working directory moves, so any
5296+
# write whose path is not an absolute literal could land anywhere.
5297+
moved = any(not in_string[m.start()] for m in _CHDIR_RE.finditer(program))
52825298
for m in _CALL_RE.finditer(program):
52835299
if in_string[m.start()]: # ``open(p, 'w')`` quoted as DATA (a fixture, a message) is not a call
52845300
continue
@@ -5319,6 +5335,8 @@ def _program_opaque_write(program: str) -> bool:
53195335
for e in exprs:
53205336
if not _LITERAL_ARG_RE.fullmatch(e) and _names_control_plane(e):
53215337
return True
5338+
if moved and not re.fullmatch(r'''\s*['"](?:/|~)[^'"]*['"]\s*''', e):
5339+
return True
53225340
return False
53235341

53245342

0 commit comments

Comments
 (0)