Skip to content
Draft
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8fd4112
test(browser): require bounded Chromium process-tree RSS
seonghobae Aug 11, 2026
f73f2a6
test(browser): measure bounded Chromium process-tree RSS
seonghobae Aug 11, 2026
a64d3b1
test(browser): reject unreliable live proc children discovery
seonghobae Aug 11, 2026
675e573
fix(browser): discover Chromium process set from proc status
seonghobae Aug 11, 2026
623ac23
test(browser): require one sampled process evidence snapshot
seonghobae Aug 11, 2026
1d12a1c
fix(browser): sample Chromium lineage and RSS in one proc sweep
seonghobae Aug 11, 2026
ec5a34a
docs(changelog): record sampled Chromium process-set evidence
seonghobae Aug 11, 2026
85df938
test(browser): reproduce missing descendant RSS failure
seonghobae Aug 11, 2026
cbf922f
fix(browser): tolerate nonresident Chromium descendants
seonghobae Aug 11, 2026
015e4a5
test(browser): reject ambiguous sampled VmRSS evidence
seonghobae Aug 11, 2026
ef6f233
test(browser): restore green process-set evidence lane after RED probe
seonghobae Aug 11, 2026
ceb1c72
test(browser): distinguish absent from ambiguous VmRSS
seonghobae Aug 11, 2026
e5fabfd
fix(browser): fail closed on ambiguous VmRSS evidence
seonghobae Aug 11, 2026
3ab127f
chore(browser): sync hardened fixture contract
seonghobae Aug 11, 2026
d502385
test(browser): require inherited URL invariance
seonghobae Aug 12, 2026
d099b81
fix(browser): preserve URL invariant in process-set evidence
seonghobae Aug 12, 2026
6ba2d03
chore(browser): align process-set evidence with current resource parent
seonghobae Aug 12, 2026
e2e3a52
merge: align Chromium process-set evidence with current prerequisite
seonghobae Aug 15, 2026
d6b9596
merge: realign Chromium process-set evidence with current resource pr…
seonghobae Aug 17, 2026
91bf621
docs(browser): preserve process-set evidence changelog
seonghobae Aug 17, 2026
b0efd75
test(browser): require one RSS evidence snapshot
seonghobae Aug 18, 2026
487169d
Merge 0104bd92be53e7b6db1023dc03218f788880dce1 into 61c2fb8c2aeda5842…
seonghobae Aug 20, 2026
0ac2da2
Merge b0efd75b63435d30d8991ba5265bc1a063a993c4 into 487169d2ac045fbcf…
seonghobae Aug 20, 2026
7644e41
fix(mv3): sample browser rss from one snapshot
seonghobae Aug 20, 2026
7861d88
fix(mv3): reject unsafe fixture and proc paths
seonghobae Aug 21, 2026
bd2e6cd
chore(stack): realign process-set evidence on current resource parent
seonghobae Aug 26, 2026
15931e4
fix(stack): restore current resource prerequisite tree
seonghobae Aug 26, 2026
32c8e6d
test(browser): restore bounded process-set regressions
seonghobae Aug 26, 2026
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
115 changes: 115 additions & 0 deletions tests/test_agent_task_pinned_chrome_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

from __future__ import annotations

import http.client
import inspect
import pathlib
import runpy
import tempfile
import unittest
import unittest.mock

ROOT = pathlib.Path(__file__).resolve().parents[1]
RUNNER = ROOT / "scripts" / "ci" / "run_mv3_compatibility.py"
Expand Down Expand Up @@ -137,6 +140,118 @@ def test_agent_task_records_real_bounded_resource_evidence(self) -> None:
with self.subTest(expected=expected):
self.assertIn(expected, runner)

def test_agent_task_records_bounded_chromium_process_tree_rss(self) -> None:
"""The evidence runner must measure one bounded sampled process-set snapshot."""

namespace = runpy.run_path(str(RUNNER), run_name="agent_task_process_tree_contract")
runner = RUNNER.read_text(encoding="utf-8")
for expected in (
"MAX_BROWSER_PROCESS_TREE_SIZE",
"MAX_PROC_PROCESS_SCAN_SIZE",
"_parse_linux_proc_status_process_identity",
"_parse_linux_proc_status_optional_rss_bytes",
"_snapshot_linux_process_evidence",
"_discover_linux_process_tree_ids",
"_sample_linux_process_snapshot_rss_bytes",
"_sample_linux_process_set_rss_bytes",
):
with self.subTest(expected=expected):
self.assertIn(expected, namespace)
for expected in (
'"chromium_process_count"',
'"chromium_process_set_rss_bytes"',
'"failure_type"',
):
with self.subTest(expected=expected):
self.assertIn(expected, runner)

def test_process_tree_and_rss_use_one_sampled_process_snapshot(self) -> None:
"""Root and descendant RSS must come from the same bounded status snapshot."""

namespace = runpy.run_path(str(RUNNER), run_name="agent_task_one_snapshot_contract")
browser_pass_source = inspect.getsource(namespace["_run_agent_task_browser_pass"])
self.assertEqual(browser_pass_source.count("_snapshot_linux_process_evidence()"), 1)
self.assertIn("_sample_linux_process_snapshot_rss_bytes", browser_pass_source)
self.assertNotIn("_sample_linux_process_rss_bytes(browser_process_id)", browser_pass_source)

def test_process_tree_helpers_are_bounded_and_fail_closed(self) -> None:
"""Sampled lineage must be deterministic and reject malformed membership/evidence."""

namespace = runpy.run_path(str(RUNNER), run_name="agent_task_process_helper_contract")
parse_identity = namespace["_parse_linux_proc_status_process_identity"]
parse_optional_rss = namespace["_parse_linux_proc_status_optional_rss_bytes"]
discover = namespace["_discover_linux_process_tree_ids"]
sample_set = namespace["_sample_linux_process_set_rss_bytes"]

self.assertEqual(parse_identity("Pid:\t10\nPPid:\t1\n"), (10, 1))
self.assertIsNone(parse_optional_rss("Name:\tchrome\nPid:\t10\nPPid:\t1\n"))
self.assertEqual(parse_optional_rss("VmRSS:\t7 kB\n"), 7 * 1024)
with self.assertRaises(ValueError):
parse_optional_rss("VmRSS:\t7 kB\nVmRSS:\t8 kB\n")

evidence = {
10: (1, 100),
12: (10, None),
11: (10, 200),
13: (11, 300),
}
self.assertEqual(discover(10, evidence), (10, 11, 12, 13))
self.assertEqual(sample_set((10, 11, 12, 13), evidence), 600)
with self.assertRaises(ValueError):
sample_set((10, 10), evidence)
with self.assertRaises(ValueError):
sample_set((10, 99), evidence)
with self.assertRaises(RuntimeError):
discover(99, evidence)

def test_process_snapshot_ignores_symlinked_proc_entries(self) -> None:
"""The proc snapshot must not follow a symlink presented as a PID entry."""

namespace = runpy.run_path(str(RUNNER), run_name="agent_task_proc_symlink_contract")
with tempfile.TemporaryDirectory() as directory:
temporary_root = pathlib.Path(directory)
target = temporary_root / "target"
target.mkdir()
(target / "status").write_text(
"Name:\tchrome\nPid:\t123\nPPid:\t1\nVmRSS:\t1 kB\n",
encoding="utf-8",
)
symlinked_entry = temporary_root / "123"
symlinked_entry.symlink_to(target, target_is_directory=True)
with unittest.mock.patch.object(
pathlib.Path, "iterdir", return_value=iter((symlinked_entry,))
):
evidence = namespace["_snapshot_linux_process_evidence"]()

self.assertEqual(evidence, {})

def test_fixture_server_does_not_follow_symlinks_outside_fixture_root(self) -> None:
"""The controlled fixture server must not disclose a linked outside file."""

namespace = runpy.run_path(str(RUNNER), run_name="agent_task_fixture_symlink_contract")
with tempfile.TemporaryDirectory() as directory:
temporary_root = pathlib.Path(directory)
fixture_root = temporary_root / "fixture"
fixture_root.mkdir()
(fixture_root / "index.html").write_text("fixture", encoding="utf-8")
secret_path = temporary_root / "secret.txt"
secret_path.write_text("not-for-the-fixture", encoding="utf-8")
(fixture_root / "linked.txt").symlink_to(secret_path)
server, thread = namespace["_start_fixture_server"](fixture_root)
try:
connection = http.client.HTTPConnection(
"127.0.0.1", server.server_port, timeout=2
)
connection.request("GET", "/linked.txt")
response = connection.getresponse()
body = response.read()
connection.close()
finally:
namespace["_stop_fixture_server"](server, thread)

self.assertIn(response.status, {403, 404})
self.assertNotIn(b"not-for-the-fixture", body)

def test_linux_rss_parser_is_strict_and_overflow_safe(self) -> None:
"""Runner-side RSS evidence must not accept ambiguous proc status input."""

Expand Down
Loading