Skip to content
Open
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
11 changes: 6 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -175,15 +175,16 @@ RUN pip install --no-cache-dir --upgrade pip && \

RUN crawl4ai-setup

RUN playwright install --with-deps

RUN mkdir -p /home/appuser/.cache/ms-playwright \
RUN playwright install --with-deps \
&& mkdir -p /home/appuser/.cache/ms-playwright \
&& cp -r /root/.cache/ms-playwright/chromium-* \
/root/.cache/ms-playwright/chromium_headless_shell-* \
/home/appuser/.cache/ms-playwright/ \
&& chown -R appuser:appuser /home/appuser/.cache/ms-playwright
&& chown -R appuser:appuser /home/appuser/.cache/ms-playwright \
&& rm -rf /root/.cache/ms-playwright

RUN crawl4ai-doctor
RUN crawl4ai-doctor \
&& find "${APP_HOME}" -maxdepth 1 -type f -name '*.core' -delete

# Ensure all cache directories belong to appuser
# This fixes permission issues with .cache/url_seeder and other runtime cache dirs
Expand Down
24 changes: 21 additions & 3 deletions deploy/docker/tests/test_security_container_posture.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ def test_no_redis_expose(self, dockerfile):
stripped = line.strip()
if stripped.startswith("#"):
continue
assert not re.match(r"EXPOSE\s+.*\b6379\b", stripped), \
"redis port 6379 must not be EXPOSEd"
assert not re.match(
r"EXPOSE\s+.*\b6379\b", stripped
), "redis port 6379 must not be EXPOSEd"

def test_app_dir_root_owned_readonly(self, dockerfile):
assert "chown -R root:root ${APP_HOME}" in dockerfile
Expand All @@ -66,10 +67,27 @@ def test_playwright_headless_shell_is_copied_to_runtime_cache(self, dockerfile):
dockerfile,
re.DOTALL,
)
assert cache_copy, "Dockerfile must copy Playwright artifacts into appuser's cache"
assert (
cache_copy
), "Dockerfile must copy Playwright artifacts into appuser's cache"
assert "chromium-*" in cache_copy.group("artifacts")
assert "chromium_headless_shell-*" in cache_copy.group("artifacts")

def test_build_artifacts_removed_before_layer_commit(self, dockerfile):
for command, cleanup in (
("playwright install --with-deps", "rm -rf /root/.cache/ms-playwright"),
("crawl4ai-doctor", "-name '*.core' -delete"),
):
layer = re.search(
rf"^RUN {re.escape(command)}(?P<body>(?:(?!^[A-Z]+\s).)*)",
dockerfile,
re.MULTILINE | re.DOTALL,
)
assert layer, f"Dockerfile must run {command}"
assert cleanup in layer.group(
"body"
), f"{cleanup} must run in the {command} layer"

def test_runs_as_non_root(self, dockerfile):
assert re.search(r"^USER\s+appuser", dockerfile, re.MULTILINE)

Expand Down
Loading