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
10 changes: 10 additions & 0 deletions tests/platform/test_lab_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ def test_execute_lab_verifier_rejects_shell_only_lab(tmp_path: Path) -> None:
assert result.error == "No verify.py for lab 9.8"


def test_execute_lab_verifier_rejects_lab_path_escape(tmp_path: Path) -> None:
labs_root = tmp_path / "labs"
labs_root.mkdir()

result = execute_lab_verifier("../escape", labs_root=labs_root)

assert result.passed is False
assert result.error == "Invalid lab path for lab ../escape"


def test_load_lab_manifest_supports_response_phase_shape() -> None:
manifest = load_lab_manifest(
Path("labs/tier-7-detection-response/7.5-threat-modeling")
Expand Down
26 changes: 23 additions & 3 deletions weaklink_platform/lab_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import base64
import json
import os
import re
import shlex
import subprocess
from dataclasses import dataclass, field
Expand All @@ -19,6 +20,7 @@
DEFAULT_REPOS_ROOT = Path("/repos")
DEFAULT_WORKSPACE_ROOT = Path("/workspace")
DEFAULT_LABS_ROOT = Path("/opt/labs")
LAB_ID_PATTERN = re.compile(r"\d+\.\d+")


def _package_root() -> Path:
Expand Down Expand Up @@ -226,6 +228,12 @@
return env


def _validated_lab_id(lab_id: str) -> str:
if not LAB_ID_PATTERN.fullmatch(lab_id):

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on a
user-provided value
may run slow on strings with many repetitions of '0'.
raise ValueError(f"Invalid lab id: {lab_id}")
return lab_id


def main_init(callback: InitHook, argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument("--json", action="store_true")
Expand Down Expand Up @@ -386,10 +394,22 @@
labs_root: Path = DEFAULT_LABS_ROOT,
timeout: int = 30,
) -> VerificationResult:
resolved_lab_dir = lab_dir or (labs_root / lab_id)
env = _verifier_env(lab_id, resolved_lab_dir)
try:
resolved_labs_root = labs_root.resolve(strict=False)
safe_lab_id = _validated_lab_id(lab_id)
expected_lab_dir = (resolved_labs_root / safe_lab_id).resolve(strict=False)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
if lab_dir is not None:
provided_lab_dir = lab_dir.resolve(strict=False)
if provided_lab_dir != expected_lab_dir:
raise ValueError(f"Lab directory does not match lab id: {lab_id}")
resolved_lab_dir = expected_lab_dir
resolved_lab_dir.relative_to(resolved_labs_root)
python_verifier = (resolved_lab_dir / "verify.py").resolve(strict=False)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
python_verifier.relative_to(resolved_lab_dir)
except ValueError:
return VerificationResult(False, (), error=f"Invalid lab path for lab {lab_id}")

python_verifier = resolved_lab_dir / "verify.py"
env = _verifier_env(lab_id, resolved_lab_dir)

try:
if python_verifier.exists():
Expand Down
Loading