Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Dockerfile.antseed
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ RUN apt-get update \
RUN npm install -g @antseed/cli@0.1.128 pg@8.16.3
ENV NODE_PATH=/usr/local/lib/node_modules

COPY antseed/db.js antseed/store.js antseed/amount.js antseed/write-market.js antseed/write-status.js antseed/control.js antseed/reclaim.mjs antseed/entrypoint.sh /usr/local/lib/antseed/
# Every non-test file under antseed/ — an explicit list silently ships a module
# whose `require('./x.js')` has no target, and the control server then dies at
# import time with the wallet endpoints 502ing (which is exactly what shipping
# ids.js/queue.js without updating this line did). tests/test_antseed_node.py
# pins that every local require resolves inside the image.
COPY antseed/*.js antseed/*.mjs antseed/entrypoint.sh /usr/local/lib/antseed/
RUN rm -f /usr/local/lib/antseed/*.test.js
RUN chmod +x /usr/local/lib/antseed/entrypoint.sh

ENV ANTSEED_DATA_DIR=/data
Expand Down
40 changes: 40 additions & 0 deletions tests/test_antseed_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
exercises BOTH formats; running it from pytest means it executes wherever the
suite runs, not just by hand.
"""
import re
import shutil
import subprocess
from pathlib import Path
Expand Down Expand Up @@ -58,3 +59,42 @@ def test_antseed_reclaim_channel_selection():
reclaim.mjs itself deep-imports @antseed/cli internals that exist only in the
sidecar image, so the selection logic lives here where it can be tested."""
_run_node_test("antseed/ids.test.js")


_LOCAL_IMPORT = re.compile(
r"""(?:require\(\s*|from\s+)['"](\./[^'"]+)['"]""")


def test_every_local_import_is_shipped_in_the_sidecar_image():
"""Every `require('./x.js')` in a shipped sidecar module resolves to another
shipped file.

The node tests above run against the REPO, so they pass whether or not a file
reaches the image. That gap shipped a control.js requiring ./ids.js into an
image built from an explicit COPY list that named neither ids.js nor queue.js:
the control server died at import (`Cannot find module './ids.js'`), :8379
never bound, and every wallet endpoint 502'd — with the only symptom a
Cloudflare error page on the deposit button. Nothing else noticed, because the
market/status writers do not import it and the buyer proxy is a separate
process.
"""
antseed = _REPO_ROOT / "antseed"
shipped = {p.name for p in antseed.iterdir()
if p.suffix in (".js", ".mjs") and not p.name.endswith(".test.js")}
assert "control.js" in shipped and "ids.js" in shipped, shipped

missing = []
for name in sorted(shipped):
for spec in _LOCAL_IMPORT.findall((antseed / name).read_text()):
target = spec[2:] # drop the leading "./"
if target not in shipped:
missing.append(f"{name} imports {spec!r}, which is not shipped")
assert not missing, "\n".join(missing)

# The COPY must be pattern-based; an explicit list is what drifted.
copy_lines = [ln for ln in (_REPO_ROOT / "Dockerfile.antseed").read_text().splitlines()
if ln.startswith("COPY ") and "/usr/local/lib/antseed/" in ln]
assert copy_lines, "Dockerfile.antseed has no COPY into /usr/local/lib/antseed/"
assert any("antseed/*.js" in ln for ln in copy_lines), (
"COPY must glob antseed/*.js — naming files individually is how ids.js "
f"and queue.js were left out of the image: {copy_lines}")
Comment on lines +84 to +100

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Assert the complete sidecar packaging contract.

The test requires only control.js and ids.js at Line [84]. The Dockerfile check at Lines [98-100] only requires antseed/*.js. It does not protect queue.js, antseed/*.mjs, entrypoint.sh, or the test-file cleanup. A future packaging regression could therefore pass this test.

Proposed assertions
-    assert "control.js" in shipped and "ids.js" in shipped, shipped
+    assert {"control.js", "ids.js", "queue.js"} <= shipped, shipped

+    dockerfile_text = (_REPO_ROOT / "Dockerfile.antseed").read_text()
-    copy_lines = [ln for ln in (_REPO_ROOT / "Dockerfile.antseed").read_text().splitlines()
+    copy_lines = [ln for ln in dockerfile_text.splitlines()
                   if ln.startswith("COPY ") and "/usr/local/lib/antseed/" in ln]
...
-    assert any("antseed/*.js" in ln for ln in copy_lines), (
-        "COPY must glob antseed/*.js — naming files individually is how ids.js "
-        f"and queue.js were left out of the image: {copy_lines}")
+    required_sources = (
+        "antseed/*.js",
+        "antseed/*.mjs",
+        "antseed/entrypoint.sh",
+    )
+    assert all(
+        any(source in line for line in copy_lines)
+        for source in required_sources
+    ), f"COPY must include {required_sources}: {copy_lines}"
+    assert "*.test.js" in dockerfile_text
🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 87-87: XPath query is request-/variable-derived; use parameterized XPath to prevent injection.
Context: _LOCAL_IMPORT.findall((antseed / name).read_text())
Note: [CWE-643] Improper Neutralization of Data within XPath Expressions ('XPath Injection').

(xpath-injection-python)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_antseed_node.py` around lines 84 - 100, Expand the packaging
assertions in the test around the shipped-file and Dockerfile checks to cover
the complete sidecar contract: require queue.js and all expected antseed/*.mjs
files alongside control.js and ids.js, verify entrypoint.sh is included, and
assert test files are removed from the packaged output. Ensure the
Dockerfile.antseed COPY rules are pattern-based for each required artifact and
preserve the existing local-import closure validation.