Skip to content

Proof: закрепить безопасные нативные координаты - #509

Closed
lemone112 wants to merge 9 commits into
agent/build-transportfrom
agent/mpfi-observer-placement
Closed

Proof: закрепить безопасные нативные координаты#509
lemone112 wants to merge 9 commits into
agent/build-transportfrom
agent/mpfi-observer-placement

Conversation

@lemone112

@lemone112 lemone112 commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Что меняется

  • Нативная проверка Docker и cgroup-координат использует read-free descriptor mode: Linux O_PATH и O_NOFOLLOW на каждом сегменте пути.
  • Поэтому допустимы CLI только с правом исполнения и каталог только с правом поиска; symbolic link по-прежнему fail-closed.
  • Три повторения открытия дочерней cgroup сведены в один локальный helper без нового публичного слоя.
  • Linux без положительного O_PATH не получает тихий fallback: probe возвращает typed unsupported path.

Причина

O_RDONLY требовал право чтения там, где нативная preflight-проверка читает только metadata. Это неверно отклоняло корректную executable-only/search-only координату и не соответствовало заявленной семантике точного descriptor-relative path.

Границы

Docker capability V1 связывает pathname, а не неизменный file object между probe и BUILD. CLI и daemon остаются явной unsealed host boundary. Этот PR не заявляет выполненный Linux/VM integration gate.

Проверка на текущей голове

  • RED→GREEN: intermediate/final symbolic links, executable-only CLI, search-only cgroup/Docker segments, fail-closed O_PATH=None/0.
  • Anti-vacuum: marker связывает Linux O_PATH с каждым фактическим open; намеренные мутации к O_RDONLY и hard-coded O_EXEC делают оба пути красными независимо от UID.
  • Arb gate normal и -O: 193 tests, exact 11-skip manifest.
  • test_receipt.py normal и -O: 19 tests, 1 declared external-integration skip.
  • test_build.py normal и -O: 45 tests.
  • Shared proof discovery normal и -O: 156 tests, 1 declared external-integration skip.
  • git show --check чист.

Stacked on agent/build-transport (#508); merge только после parent chain и отдельного external VM gate.

Join contract

Этот срез намеренно не является предком #510/#511. Перед началом M2b successor обязан явно объединить этот head с завершённым #511 и повторно прогнать общий executor/receipt gate; #511 сам по себе не содержит O_PATH/native execution hardening из этого PR.

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

Проверь exact head b950f3e. Фокус: shared executor boundary, отсутствие private cross-module coupling, O_NOFOLLOW/FD cleanup/short-write fail-closed semantics и отсутствие engine semantics в executor. Не проверяй и не запускай Actions/runner infrastructure.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Изменения добавляют descriptor-relative проверки Docker CLI и cgroup с O_NOFOLLOW. Размещение observer перенесено в публичный executor API. Arb controller канонизирует входные координаты и преобразует ошибки в TypeError. Тесты покрывают symlink, hostile paths, ошибки записи и управление дескрипторами.

Changes

Границы безопасности путей

Layer / File(s) Summary
Координата и probe Docker CLI
proof/region/v1/build/transport.py, proof/region/v1/tests/test_build.py, proof/region/v1/PROTOCOL.md, proof/region/v1/arb/tests/test_transport.py
Docker CLI получает строгую абсолютную координату. Путь открывается по компонентам без перехода по symlink. Probe проверяет конечный descriptor через fstat. Тесты проверяют допустимые и hostile значения, включая symlink-алиасы.
Безопасный cgroup API
proof/region/v1/executor.py, proof/region/v1/tests/test_executor.py, proof/region/v1/PROTOCOL.md
cgroup parent проходит каноническую проверку. Каталоги и cgroup.procs открываются descriptor-relative с O_NOFOLLOW. enter_observer_cgroup_v1 проверяет каталог observer, выполняет запись и корректно закрывает дескрипторы.
Интеграция Arb с executor API
proof/region/v1/arb/receipt.py, proof/region/v1/arb/tests/test_receipt.py, proof/region/v1/arb/tests/gate.py
SourceBoundArbControllerV1 использует публичные проверки Docker и cgroup. Размещение observer вызывает executor.enter_observer_cgroup_v1. Тесты проверяют отказ до создания backend, типизированную ошибку и одноразовое состояние контроллера.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ArbController as SourceBoundArbControllerV1
  participant DockerTransport as docker_command_coordinate_v1
  participant LinuxExecutor as canonical_cgroup_parent_v1
  participant Cgroup as enter_observer_cgroup_v1
  ArbController->>DockerTransport: validate and store Docker coordinate
  ArbController->>LinuxExecutor: canonicalize cgroup parent
  ArbController->>Cgroup: enter observer cgroup during execution
  Cgroup-->>ArbController: success or typed placement failure
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.28% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Заголовок точно отражает основное изменение: закрепление безопасных нативных координат для Docker CLI и cgroup.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/mpfi-observer-placement

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Commit identity =='
git show -s --format='commit=%H%nparents=%P%nsubject=%s' b950f3e
echo

echo '== Changed paths =='
git diff-tree --no-commit-id --name-status -r b950f3e
echo

echo '== Diff stat =='
git diff --stat b950f3e^ b950f3e
echo

echo '== Diff =='
git diff --find-renames --find-copies --no-ext-diff b950f3e^ b950f3e
echo

echo '== Candidate symbols and module references =='
rg -n -C 3 --hidden \
  -g '!**/.git/**' \
  -e '\benter_observer_cgroup_v1\b' \
  -e '\bO_NOFOLLOW\b' \
  -e '\bobserver[_ -]?cgroup\b' \
  -e '\bArb\b|\bMPFI\b|\breceipt\b|\bRUN\b' \
  .

Length of output: 180830


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== executor import surface =='
sed -n '1,105p' proof/region/v1/executor.py
echo

echo '== executor placement implementation and adjacent cgroup code =='
sed -n '1375,1460p' proof/region/v1/executor.py
echo

echo '== receipt imports and complete execute ordering =='
sed -n '1,85p' proof/region/v1/arb/receipt.py
sed -n '590,735p' proof/region/v1/arb/receipt.py
echo

echo '== Structural definitions and direct call sites =='
ast-grep run --lang python --pattern 'def enter_observer_cgroup_v1($$$): $$$' proof/region/v1
ast-grep run --lang python --pattern '$_MODULE.enter_observer_cgroup_v1($$$)' proof/region/v1
echo

echo '== Duplicate protocol operations outside executor =='
rg -n -C 2 --glob '*.py' \
  -e 'def\s+_?enter_observer_cgroup_v1\b' \
  -e 'parent\s*/\s*["'\'']observer["'\'']' \
  -e 'b?["'\'']cgroup\.procs["'\'']' \
  proof/region/v1 \
  | grep -vE '^(proof/region/v1/executor\.py|proof/region/v1/tests/|proof/region/v1/arb/tests/)' \
  || true

Length of output: 16382


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from __future__ import annotations

import ast
from pathlib import Path

root = Path("proof/region/v1")
for path in sorted(root.rglob("*.py")):
    if "tests" in path.parts:
        continue
    tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
    for node in ast.walk(tree):
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.name == "enter_observer_cgroup_v1":
            print(f"DEFINITION {path}:{node.lineno}")
        if (
            isinstance(node, ast.Call)
            and isinstance(node.func, ast.Attribute)
            and node.func.attr == "enter_observer_cgroup_v1"
        ):
            receiver = (
                node.func.value.id
                if isinstance(node.func.value, ast.Name)
                else ast.unparse(node.func.value)
            )
            print(f"CALL {path}:{node.lineno} receiver={receiver}")
        if (
            isinstance(node, ast.Call)
            and isinstance(node.func, ast.Attribute)
            and node.func.attr == "open"
            and any(
                isinstance(argument, ast.Constant) and argument.value == b"cgroup.procs"
                for argument in node.args
            )
        ):
            print(f"CGROUP_PROCS_OPEN {path}:{node.lineno}")
PY

echo
echo '== Executor engine-neutrality tokens, excluding tests and workflows =='
rg -n -i --glob '*.py' \
  -e '\barb\b' -e '\bmpfi\b' -e '\breceipt\b' -e '\bformula\b' -e '\bcomparator\b' \
  proof/region/v1/executor.py \
  || true

echo
echo '== Placement test body =='
sed -n '1500,1582p' proof/region/v1/tests/test_executor.py

Length of output: 5605


@lemone112 Проверил exact head b950f3e2e8611b3f808f0fb19e7db87e542cf15a.

Замечаний в указанной области нет.

  • executor.enter_observer_cgroup_v1 является единственной non-test реализацией placement protocol.
  • Arb вызывает только этот public versioned API. Private cross-module вызова или копии descriptor protocol не осталось.
  • API применяет O_NOFOLLOW к observer и cgroup.procs.
  • API проверяет directory FD, отклоняет short write и закрывает оба FD на всех путях после успешного открытия.
  • Arb преобразует ошибку placement в OBSERVER_PLACEMENT_FAILED. RUN backend не создаётся до успешного placement.
  • executor.py не содержит Arb, MPFI, receipt, formula или comparator semantics.

Я выполнил только статический анализ. Я не запускал тесты, Actions или runner infrastructure.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@lemone112
lemone112 force-pushed the agent/mpfi-observer-placement branch from b950f3e to c63b3d5 Compare August 1, 2026 08:02
@lemone112

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

Stack rebased safely onto #508; exact new head c63b3d5. Review only observer cgroup placement and its final inventory pins; no workflow/runner scope.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

head_commit="$(git rev-parse c63b3d5)"
parent_commit="$(git rev-parse "${head_commit}^")"

echo "== Commit identity =="
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$head_commit"
echo

echo "== Changed paths =="
git diff-tree --no-commit-id --name-status -r "$head_commit"
echo

echo "== Focused diff: observer placement and inventory/reference pins =="
git diff --find-renames --find-copies --no-ext-diff "$parent_commit" "$head_commit" -- \
  'proof/region/v1/executor.py' \
  'proof/region/v1/arb/receipt.py' \
  'proof/region/v1/reference.py' \
  'proof/region/v1/**/inventory*' \
  'proof/region/v1/**/manifest*' \
  'proof/region/v1/**/test*' \
  || true
echo

echo "== Locate candidate source files =="
fd -t f -e py . proof/region/v1 | sort
echo

echo "== Definitions, call sites, and cgroup protocol operations outside tests =="
python3 - <<'PY'
import ast
from pathlib import Path

root = Path("proof/region/v1")
for path in sorted(root.rglob("*.py")):
    if "tests" in path.parts:
        continue
    try:
        tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
    except (OSError, SyntaxError) as exc:
        print(f"PARSE_ERROR {path}: {exc}")
        continue
    for node in ast.walk(tree):
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.name == "enter_observer_cgroup_v1":
            print(f"DEFINITION {path}:{node.lineno}")
        elif isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute):
            if node.func.attr == "enter_observer_cgroup_v1":
                receiver = ast.unparse(node.func.value)
                print(f"CALL {path}:{node.lineno} receiver={receiver}")
            if node.func.attr == "open" and any(
                isinstance(arg, ast.Constant) and arg.value == b"cgroup.procs"
                for arg in node.args
            ):
                print(f"CGROUP_PROCS_OPEN {path}:{node.lineno}")
PY
echo

echo "== Engine-semantic imports/references in executor =="
rg -n -i -C 2 --glob 'executor.py' \
  '\b(arb|mpfi|receipt|formula|comparator|run backend)\b' \
  proof/region/v1 || true
echo

echo "== Reference and inventory pin candidates =="
rg -n -C 3 --glob '*.py' --glob '*.md' --glob '*.txt' \
  '\benter_observer_cgroup_v1\b|\bobserver cgroup\b|\bobserver_cgroup\b|\bO_NOFOLLOW\b' \
  proof/region/v1 \
  | head -n 1200

Length of output: 35380


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Gate inventory algorithm and pins =="
sed -n '1,180p' proof/region/v1/arb/tests/gate.py
echo

echo "== Shared build inventory pin validation =="
sed -n '1,125p' proof/region/v1/tests/test_build.py
echo

echo "== Arb imports and placement-to-RUN ordering =="
sed -n '1,90p' proof/region/v1/arb/receipt.py
sed -n '630,690p' proof/region/v1/arb/receipt.py
echo

echo "== Protocol ownership claim =="
sed -n '205,232p' proof/region/v1/PROTOCOL.md
echo

echo "== Independently calculate Arb test inventory and order candidates =="
python3 - <<'PY'
from __future__ import annotations

import ast
import hashlib
from pathlib import Path

test_dir = Path("proof/region/v1/arb/tests")
tests = sorted(
    path.relative_to(test_dir).as_posix()
    for path in test_dir.glob("test_*.py")
)
print("TEST_FILES")
print("\n".join(tests))
print("FILE_INVENTORY_SHA256", hashlib.sha256("\n".join(tests).encode()).hexdigest())

# Extract unittest-style methods statically. Do not import or execute project code.
methods: list[str] = []
for path in sorted(test_dir.glob("test_*.py")):
    tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
    for cls in (node for node in tree.body if isinstance(node, ast.ClassDef)):
        if any(
            (isinstance(base, ast.Attribute) and base.attr == "TestCase")
            or (isinstance(base, ast.Name) and base.id == "TestCase")
            for base in cls.bases
        ):
            for item in cls.body:
                if isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef)) and item.name.startswith("test"):
                    methods.append(f"{path.name}:{cls.name}.{item.name}")
methods.sort()
payload = "\n".join(methods)
print("STATIC_TEST_METHOD_COUNT", len(methods))
print("STATIC_TEST_METHOD_ORDER_SHA256", hashlib.sha256(payload.encode()).hexdigest())
print("STATIC_TEST_METHOD_INVENTORY_SHA256", hashlib.sha256("\n".join(methods).encode()).hexdigest())
PY
echo

echo "== Cross-module imports from executor, excluding tests =="
rg -n -C 2 --glob '*.py' \
  '^\s*(from|import)\s+.*\b(arb|receipt|mpfi)\b' \
  proof/region/v1/executor.py || true

Length of output: 19080


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Outer-oracle inventory assertions =="
rg -n -A18 -B8 \
  'ARB_(INVENTORY_SHA256_V1|ORDER_SHA256_V1|TEST_COUNT_V1)|test_inventory_sha256_v1|full_suite_v1' \
  proof/region/v1/tests/test_build.py
echo

echo "== Static reconstruction of gate test IDs =="
python3 - <<'PY'
from __future__ import annotations

import ast
import hashlib
from pathlib import Path

sources = [Path("proof/region/v1/tests/test_executor.py")]
sources.extend(sorted(Path("proof/region/v1/arb/tests").glob("test_*.py")))

def dotted_module(path: Path) -> str:
    return path.stem

def test_case_classes(tree: ast.Module) -> set[str]:
    classes = {
        node.name: node
        for node in ast.walk(tree)
        if isinstance(node, ast.ClassDef)
    }
    derived: set[str] = set()
    changed = True
    while changed:
        changed = False
        for name, node in classes.items():
            if name in derived:
                continue
            bases = {
                base.id for base in node.bases if isinstance(base, ast.Name)
            } | {
                base.attr for base in node.bases if isinstance(base, ast.Attribute)
            }
            if "TestCase" in bases or bases & derived:
                derived.add(name)
                changed = True
    return derived

test_ids: list[str] = []
for path in sources:
    tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
    classes = {
        node.name: node
        for node in ast.walk(tree)
        if isinstance(node, ast.ClassDef)
    }
    for class_name in sorted(test_case_classes(tree)):
        node = classes[class_name]
        for member in node.body:
            if isinstance(member, (ast.FunctionDef, ast.AsyncFunctionDef)) and member.name.startswith("test"):
                test_ids.append(f"{dotted_module(path)}.{class_name}.{member.name}")

test_ids.sort()
preimage = b"".join(test_id.encode("utf-8") + b"\n" for test_id in test_ids)
print("COUNT", len(test_ids))
print("INVENTORY_SHA256", hashlib.sha256(preimage).hexdigest())
print("ORDER_SHA256", hashlib.sha256("\n".join(test_ids).encode("utf-8")).hexdigest())
print("FIRST_IDS")
print("\n".join(test_ids[:8]))
print("LAST_IDS")
print("\n".join(test_ids[-8:]))
PY
echo

echo "== Exact pin literals =="
rg -n -C 2 \
  '2571be2b0998ed9347fd2f51efd5eeedcd8283ccf8f0bc9f24817d62c9505653|8f74e87ece46cb8411f0cb1f545dd7feb31bb00294f1d4f7fa6bf2c386c57e33|ARB_TEST_COUNT_V1 = 172' \
  proof/region/v1/arb/tests/gate.py proof/region/v1/tests/test_build.py

Length of output: 8650


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from __future__ import annotations

import ast
import hashlib
from pathlib import Path

sources = [Path("proof/region/v1/tests/test_executor.py")]
sources.extend(sorted(Path("proof/region/v1/arb/tests").glob("test_*.py")))

def module_test_case_classes(tree: ast.Module) -> dict[str, ast.ClassDef]:
    classes = {
        node.name: node for node in tree.body if isinstance(node, ast.ClassDef)
    }
    derived: set[str] = set()
    changed = True
    while changed:
        changed = False
        for name, node in classes.items():
            if name in derived:
                continue
            bases = {
                base.id for base in node.bases if isinstance(base, ast.Name)
            } | {
                base.attr for base in node.bases if isinstance(base, ast.Attribute)
            }
            if "TestCase" in bases or bases & derived:
                derived.add(name)
                changed = True
    return {name: classes[name] for name in derived}

test_ids: list[str] = []
nested_candidates: list[str] = []
for path in sources:
    tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
    module_classes = module_test_case_classes(tree)
    for class_name, node in module_classes.items():
        for member in node.body:
            if isinstance(member, (ast.FunctionDef, ast.AsyncFunctionDef)) and member.name.startswith("test"):
                test_ids.append(f"{path.stem}.{class_name}.{member.name}")
    for node in ast.walk(tree):
        if isinstance(node, ast.ClassDef) and node not in module_classes.values():
            for member in node.body:
                if isinstance(member, (ast.FunctionDef, ast.AsyncFunctionDef)) and member.name.startswith("test"):
                    nested_candidates.append(f"{path}:{node.name}.{member.name}")

test_ids.sort()
sorted_preimage = b"".join(item.encode("utf-8") + b"\n" for item in test_ids)
# unittest discovery is lexicographic by method name within each class, then
# module attributes are traversed in sorted name order. This is modeled here.
ordered_ids: list[str] = []
for path in sources:
    for class_name in sorted(module_test_case_classes(
        ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
    )):
        node = module_test_case_classes(
            ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
        )[class_name]
        for member in sorted(
            (
                item for item in node.body
                if isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef))
                and item.name.startswith("test")
            ),
            key=lambda item: item.name,
        ):
            ordered_ids.append(f"{path.stem}.{class_name}.{member.name}")

print("MODULE_LEVEL_COUNT", len(test_ids))
print("SORTED_INVENTORY_SHA256", hashlib.sha256(sorted_preimage).hexdigest())
print("DISCOVERY_ORDER_SHA256", hashlib.sha256(
    b"".join(item.encode("utf-8") + b"\n" for item in ordered_ids)
).hexdigest())
print("NESTED_EXCLUDED")
print("\n".join(sorted(nested_candidates)) or "(none)")
PY

Length of output: 3707


@lemone112 Проверил exact head c63b3d5171a465e74585e43d251bf385ed7209ba.

Замечаний в указанной области нет.

  • executor.enter_observer_cgroup_v1 остаётся единственной non-test реализацией placement protocol.
  • Arb вызывает только public versioned API. Private cross-module coupling и копии descriptor protocol отсутствуют.
  • API применяет O_NOFOLLOW к observer и cgroup.procs.
  • API проверяет directory FD, отклоняет short write и закрывает оба FD.
  • Arb преобразует любой отказ placement в OBSERVER_PLACEMENT_FAILED до создания RUN backend.
  • executor.py не содержит Arb, MPFI, receipt или engine semantics.
  • Claim в PROTOCOL.md фиксирует ownership границы.
  • Final inventory pins совпадают со статически восстановленным набором: 172 tests, inventory SHA-256 2571be…05653, order SHA-256 8f74e…57e33.

Я выполнил только статический анализ. Я не запускал тесты, Actions или runner infrastructure.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@lemone112
lemone112 force-pushed the agent/mpfi-observer-placement branch from c63b3d5 to abb8a02 Compare August 1, 2026 12:23

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with 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.

Inline comments:
In `@proof/region/v1/arb/tests/test_receipt.py`:
- Around line 304-322: Исправьте тест
test_controller_rejects_hostile_native_coordinates_on_construction: поскольку
SourceBoundArbControllerV1.__init__ отклоняет подклассы по точному типу до
вызова path-методов, переименуйте ExplodingPath и ExplodingFilesystemPath в
названия, отражающие проверку отказа подкласса, например PathSubclass; либо
перенесите проверку в прямые вызовы build_transport.docker_command_coordinate_v1
и executor.canonical_cgroup_parent_v1, если требуется тестировать враждебный
path-протокол.

In `@proof/region/v1/build/transport.py`:
- Around line 1106-1115: Update _open_docker_command_v1 to accept the already
validated command coordinate rather than a Path, and encode only that
coordinate’s path without calling docker_command_coordinate_v1 again. Update the
caller around self._command_coordinate to pass the coordinate object directly,
preserving the existing probe behavior and validation source.

In `@proof/region/v1/executor.py`:
- Around line 1491-1502: Update enter_observer_cgroup_v1 to construct both
OSError instances with explicit errno values, using the appropriate errno_module
code for the non-directory validation and short cgroup placement write,
consistent with the module’s existing cgroup error handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f048bde2-7330-4092-a509-4192f74af317

📥 Commits

Reviewing files that changed from the base of the PR and between 7a82231 and abb8a02.

📒 Files selected for processing (9)
  • proof/region/v1/PROTOCOL.md
  • proof/region/v1/arb/receipt.py
  • proof/region/v1/arb/tests/gate.py
  • proof/region/v1/arb/tests/test_receipt.py
  • proof/region/v1/arb/tests/test_transport.py
  • proof/region/v1/build/transport.py
  • proof/region/v1/executor.py
  • proof/region/v1/tests/test_build.py
  • proof/region/v1/tests/test_executor.py

Comment thread proof/region/v1/arb/tests/test_receipt.py Outdated
Comment thread proof/region/v1/build/transport.py Outdated
Comment thread proof/region/v1/executor.py Outdated

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@lemone112
lemone112 force-pushed the agent/build-transport branch from 7a82231 to 284ee16 Compare August 1, 2026 12:55
@lemone112
lemone112 force-pushed the agent/mpfi-observer-placement branch from d3b1533 to de5b427 Compare August 1, 2026 13:32
@lemone112 lemone112 changed the title Proof: централизовать observer cgroup placement Proof: закрепить безопасные нативные координаты Aug 1, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with 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.

Inline comments:
In `@proof/region/v1/tests/test_executor.py`:
- Around line 1799-1931: Разделите сценарии из
test_observer_cgroup_placement_is_exact_and_fail_closed на отдельные тестовые
методы или независимые subTest-блоки: успешное размещение, недопустимые типы,
hostile path-оператор, symlink parent, symlink-алиас компонента, symlink
observer, symlink cgroup.procs и короткая запись. Сохраните существующие
проверки и очистку ресурсов, но обеспечьте изоляцию каждого сценария, чтобы
падение одного не скрывало результаты остальных.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 51b3f029-f7f4-4475-82fd-65ad58a36b78

📥 Commits

Reviewing files that changed from the base of the PR and between abb8a02 and de5b427.

📒 Files selected for processing (9)
  • proof/region/v1/PROTOCOL.md
  • proof/region/v1/arb/receipt.py
  • proof/region/v1/arb/tests/gate.py
  • proof/region/v1/arb/tests/test_receipt.py
  • proof/region/v1/arb/tests/test_transport.py
  • proof/region/v1/build/transport.py
  • proof/region/v1/executor.py
  • proof/region/v1/tests/test_build.py
  • proof/region/v1/tests/test_executor.py

Comment on lines +1799 to +1931
def test_observer_cgroup_placement_is_exact_and_fail_closed(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary).resolve()
parent = root / "proof"
observer = parent / "observer"
observer.mkdir(parents=True)
procs = observer / "cgroup.procs"
procs.write_bytes(b"")

parent.chmod(0o111)
observer.chmod(0o111)
procs.chmod(0o222)
try:
executor.enter_observer_cgroup_v1(parent)
finally:
procs.chmod(0o644)
observer.chmod(0o755)
parent.chmod(0o755)

self.assertEqual(procs.read_bytes(), str(os.getpid()).encode("ascii"))

for invalid in (
object(),
Path("relative"),
Path("/absolute\0"),
Path("/absolute\ud800"),
"/absolute",
):
with self.subTest(invalid=invalid):
with self.assertRaises(TypeError):
executor.enter_observer_cgroup_v1(invalid) # type: ignore[arg-type]

class ExplodingPath(type(Path())):
def __truediv__(self, _other: object) -> Path:
raise RuntimeError("hostile path operator")

with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary).resolve()
parent = root / "proof"
observer = parent / "observer"
observer.mkdir(parents=True)
procs = observer / "cgroup.procs"
procs.write_bytes(b"")

executor.enter_observer_cgroup_v1(ExplodingPath(str(parent)))

self.assertEqual(procs.read_bytes(), str(os.getpid()).encode("ascii"))

with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary).resolve()
parent = root / "proof"
target = root / "target"
observer = target / "observer"
observer.mkdir(parents=True)
procs = observer / "cgroup.procs"
procs.write_bytes(b"")
parent.symlink_to(target, target_is_directory=True)

with self.assertRaises(OSError):
executor.enter_observer_cgroup_v1(parent)

self.assertEqual(procs.read_bytes(), b"")

with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary).resolve()
target = root / "target"
parent = target / "proof"
observer = parent / "observer"
observer.mkdir(parents=True)
procs = observer / "cgroup.procs"
procs.write_bytes(b"")
alias = root / "alias"
alias.symlink_to(target, target_is_directory=True)

with self.assertRaises(OSError):
executor.enter_observer_cgroup_v1(alias / "proof")

self.assertEqual(procs.read_bytes(), b"")

with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary).resolve()
parent = root / "proof"
target = root / "target"
parent.mkdir()
target.mkdir()
(target / "cgroup.procs").write_bytes(b"")
(parent / "observer").symlink_to(target, target_is_directory=True)

with self.assertRaises(OSError):
executor.enter_observer_cgroup_v1(parent)

with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary).resolve()
parent = root / "proof"
observer = parent / "observer"
observer.mkdir(parents=True)
target = root / "foreign-procs"
target.write_bytes(b"")
(observer / "cgroup.procs").symlink_to(target)

with self.assertRaises(OSError):
executor.enter_observer_cgroup_v1(parent)

with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary).resolve()
parent = root / "proof"
observer = parent / "observer"
observer.mkdir(parents=True)
(observer / "cgroup.procs").write_bytes(b"")
opened: list[int] = []
real_open = os.open

def track_open(*args: object, **kwargs: object) -> int:
descriptor = real_open(*args, **kwargs)
opened.append(descriptor)
return descriptor

with mock.patch.object(
executor.os,
"open",
side_effect=track_open,
), mock.patch.object(executor.os, "write", return_value=0):
with self.assertRaises(OSError) as caught:
executor.enter_observer_cgroup_v1(parent)
self.assertEqual(caught.exception.errno, errno.EIO)

self.assertEqual(len(opened), len(parent.parts) + 2)
for descriptor in opened:
with self.subTest(descriptor=descriptor):
with self.assertRaises(OSError) as caught:
os.fstat(descriptor)
self.assertEqual(caught.exception.errno, errno.EBADF)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Разделите тест размещения observer на отдельные тесты.

Метод содержит семь независимых сценариев: успешное размещение, отказ по типу, hostile path-оператор, symlink на parent, symlink-алиас компонента, symlink на observer, symlink на cgroup.procs и короткая запись. Первый упавший assert скрывает остальные сценарии. Выделите каждый сценарий в отдельный тест или в subTest, чтобы отчёт указывал точную причину.

🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 1809-1809: chmod sets a world-writable permission bit; restrict the mode so other users cannot modify the file (e.g. 0o644 / 0o600).
Context: procs.chmod(0o222)
Note: [CWE-276] Incorrect Default Permissions.

(world-writable-chmod-python)

🪛 Ruff (0.16.0)

[warning] 1799-1799: Too many statements (96 > 50)

(PLR0915)


[warning] 1827-1828: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)


[warning] 1828-1828: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)


[warning] 1833-1833: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 1857-1857: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)


[warning] 1873-1873: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)


[warning] 1887-1887: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)


[warning] 1899-1899: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)


[warning] 1916-1921: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)


[warning] 1921-1921: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)


[warning] 1928-1928: Use pytest.raises instead of unittest-style assertRaises

Replace assertRaises with pytest.raises

(PT027)

🤖 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 `@proof/region/v1/tests/test_executor.py` around lines 1799 - 1931, Разделите
сценарии из test_observer_cgroup_placement_is_exact_and_fail_closed на отдельные
тестовые методы или независимые subTest-блоки: успешное размещение, недопустимые
типы, hostile path-оператор, symlink parent, symlink-алиас компонента, symlink
observer, symlink cgroup.procs и короткая запись. Сохраните существующие
проверки и очистку ресурсов, но обеспечьте изоляцию каждого сценария, чтобы
падение одного не скрывало результаты остальных.

Source: Linters/SAST tools

@lemone112
lemone112 force-pushed the agent/mpfi-observer-placement branch from de5b427 to 2ff10ee Compare August 1, 2026 14:04

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Collaborator Author

Этот sibling-срез поглощён терминальным PR #514 (base main, exact head 6e8bb6445cf5564bad1b590690a43a235f4dbfbd). O_PATH/O_NOFOLLOW и observer-cgroup границы явно перенесены, дублирующий путь удалён и проверен. Закрываю как superseded; код не удалён.

@lemone112 lemone112 closed this Aug 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant