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
4 changes: 2 additions & 2 deletions scripts/ci/check-docker-workflow-isolation.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
# explicit model update and countercontrol review before gaining coverage.
QUALIFICATION_CLEANUP_SOURCES = {
"scripts/ci/native_qualification.py": "65916e73b7f0c6431c612f411e27f9429a2c37dbab287d1f17b840dee0f8d185",
"scripts/ci/native_alias_evidence.py": "b39aa4c96b7b5642b4fbb800dad0d02f0a393f7638a95220dc0c439a939ad200",
"scripts/ci/native_alias_transport.py": "053af14689c7c69d6620860390801fc900a331126f32a4f68fac6eb0e6593fef",
"scripts/ci/native_alias_evidence.py": "f5af7af8fa8eeeda1b205dfd9316d6d44277f814e850bc8356d85f1cac1c43c3",
"scripts/ci/native_alias_transport.py": "16e495ed6874f7d62f29f0f4dc11aaf9e1715bd7046823f48d3443b3ed370c74",
}


Expand Down
2 changes: 1 addition & 1 deletion scripts/ci/native-release-verify-publication.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ expected="$workflow workflow_dispatch completed success $tag $commit $run_attemp
# Both exact tags name one index.
digest() {
local subject="$1"
"$docker_bin" buildx imagetools inspect --format '{{.Manifest.Digest}}' "$subject" || fail "cannot read $subject."
"$docker_bin" buildx imagetools inspect --format '{{printf "%s" .Manifest.Digest}}' "$subject" || fail "cannot read $subject."
}
index=$(digest "$GHCR_IMAGE:$version")
[[ "$index" =~ ^sha256:[0-9a-f]{64}$ ]] || fail "exact version tag has no index digest."
Expand Down
5 changes: 4 additions & 1 deletion scripts/ci/native_alias_evidence.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,12 @@
RUNNER_PATHS = {
".github/workflows/native-image-parity.yml", ".github/workflows/published-compose.yml",
".github/workflows/native-promotion-contracts.yml",
"scripts/ci/native_alias_evidence.py", "scripts/ci/native_qualification.py", SUBJECT_PATH,
"scripts/ci/native_alias_evidence.py", "scripts/ci/native_alias_transport.py",
"scripts/ci/native_qualification.py", SUBJECT_PATH,
"scripts/ci/native-release-verify-publication.sh",
"scripts/ci/check-docker-workflow-isolation.py",
"tests/Unit/Support/native_alias_evidence_test.py", "tests/Unit/Support/native_qualification_test.py",
"tests/Feature/TaskQueueAdmissionTest.php",
"README.md",
}
MANIFEST_LIMIT = 8192
Expand Down
9 changes: 8 additions & 1 deletion scripts/ci/native_alias_transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,16 @@ def __init__(self, commands: Commands | None = None):
self.commands = commands if commands is not None else Commands()

def api(self, path: str) -> Any:
# GitHub comparisons use a three-dot separator, not a traversal
# segment. Only the immutable-source and protected-main reads used by
# the evidence predicates may contain it; every other path keeps the
# original dot/traversal refusal.
comparison = type(path) is str and re.fullmatch(
rf"repos/{REPOSITORY}/compare/[0-9a-f]{{40}}\.\.\.(?:"
r"[0-9a-f]{40}\?per_page=100|main(?:\?per_page=1)?)", path) is not None
require(type(path) is str and path.startswith(f"repos/{REPOSITORY}/")
and re.fullmatch(r"[A-Za-z0-9_./?=&-]+", path) is not None
and ".." not in path, "provider_api_path")
and (".." not in path or comparison), "provider_api_path")
return strict_json(self.commands.run(
["gh", "api", "--hostname", GITHUB_HOST, "--method", "GET", path]), JSON_LIMIT)

Expand Down
23 changes: 23 additions & 0 deletions tests/Feature/TaskQueueAdmissionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ public function test_workflow_task_polls_respect_namespace_active_lease_caps_acr

public function test_workflow_task_polls_respect_namespace_dispatch_rate_caps_across_queues(): void
{
$this->freezeTime();
Queue::fake();

config([
Expand Down Expand Up @@ -427,6 +428,7 @@ public function test_workflow_task_polls_respect_namespace_dispatch_rate_caps_ac

public function test_workflow_task_polls_respect_budget_group_dispatch_rate_caps_across_queues(): void
{
$this->freezeTime();
Queue::fake();

config([
Expand Down Expand Up @@ -498,6 +500,26 @@ public function test_workflow_task_polls_respect_budget_group_dispatch_rate_caps
->assertJsonPath('admission.workflow_tasks.server_remaining_budget_group_dispatch_capacity', 0)
->assertJsonPath('admission.workflow_tasks.server_lock_required', true)
->assertJsonPath('admission.workflow_tasks.server_lock_supported', true);

// The counter is keyed by minute. Keep the same-minute checks above
// fixed, then exercise the next bucket explicitly.
$this->travel(1)->minutes();

// The worker heartbeat has its own shorter freshness window.
$this->withHeaders($this->workerHeaders())
->postJson('/api/worker/heartbeat', [
'worker_id' => 'php-workflow-budget-group-b',
])
->assertOk()
->assertJsonPath('acknowledged', true);

$this->withHeaders($this->apiHeaders())
->getJson('/api/task-queues/external-workflows-b')
->assertOk()
->assertJsonPath('admission.workflow_tasks.status', 'accepting')
->assertJsonPath('admission.workflow_tasks.active_worker_count', 1)
->assertJsonPath('admission.workflow_tasks.server_budget_group_dispatch_count_this_minute', 0)
->assertJsonPath('admission.workflow_tasks.server_remaining_budget_group_dispatch_capacity', 1);
}

public function test_activity_task_polls_respect_queue_specific_active_lease_caps(): void
Expand Down Expand Up @@ -584,6 +606,7 @@ public function test_activity_task_polls_respect_queue_specific_active_lease_cap

public function test_activity_task_polls_respect_queue_specific_dispatch_rate_caps(): void
{
$this->freezeTime();
Queue::fake();

config([
Expand Down
104 changes: 104 additions & 0 deletions tests/Unit/Support/native_qualification_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,110 @@ def run(self, argv, limit=E.JSON_LIMIT, env=None, accepted=(0,)):


class TransportTests(unittest.TestCase):
def test_publication_verifier_uses_a_digest_template_buildx_actually_evaluates(self):
# Buildx 0.30 treats templates beginning with {{.Manifest as its
# human-readable manifest printer. Model that actual CLI behavior at
# the command boundary, without a registry, image or provider write.
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
gh = root / "gh"
gh.write_text(textwrap.dedent('''\
#!/usr/bin/env python3
import os,sys
args=sys.argv[1:]
if args[:2]==['attestation','verify']:
print('https://github.com/durable-workflow/server/actions/runs/41/attempts/1')
elif args[:1]==['api']:
path=args[1]
if '/git/ref/tags/' in path: print('commit '+os.environ['SOURCE'])
elif '/compare/' in path: print('ahead')
elif '/actions/runs/41' in path:
print('.github/workflows/native-release.yml workflow_dispatch completed success 3.2.0 '+os.environ['SOURCE']+' 1')
else: raise SystemExit(74)
else: raise SystemExit(74)
'''))
docker = root / "docker"
docker.write_text(textwrap.dedent('''\
#!/usr/bin/env python3
import os,sys
args=sys.argv[1:]
if len(args)!=6 or args[:4]!=['buildx','imagetools','inspect','--format']:
raise SystemExit(74)
if args[5] not in ['ghcr.io/durable-workflow/server:3.2.0','durableworkflow/server:3.2.0']:
raise SystemExit(74)
if args[4].startswith('{{.Manifest'):
print('Name: fixture')
print('Digest: '+os.environ['INDEX'])
elif args[4]=='{{printf "%s" .Manifest.Digest}}': print(os.environ['INDEX'])
else: raise SystemExit(74)
'''))
gh.chmod(0o700)
docker.chmod(0o700)
environment = {'PATH':os.environ['PATH'], 'SOURCE':SOURCE, 'INDEX':INDEX,
'GH':str(gh), 'DOCKER':str(docker), 'GITHUB_REPOSITORY':E.REPOSITORY,
'GHCR_IMAGE':E.GHCR_IMAGE, 'DOCKERHUB_IMAGE':E.DOCKERHUB_IMAGE,
'RELEASE_TAG':'3.2.0', 'RELEASE_RUN_ID':'41', 'RELEASE_RUN_ATTEMPT':'1'}
result = subprocess.run(['bash',str(ROOT / 'scripts/ci/native-release-verify-publication.sh')],
env=environment, capture_output=True, text=True, timeout=5, check=False)
self.assertEqual(result.returncode,0,result.stderr)
self.assertEqual(json.loads(result.stdout), {'tag':'3.2.0','source_commit':SOURCE,
'index_digest':INDEX,'run_id':41,'run_attempt':1})

def test_git_comparisons_only_allow_the_exact_source_and_main_read_shapes(self):
commands = ReadCommands(b"{}")
provider = T.GitHubRegistryProvider(commands)
prefix = "repos/" + E.REPOSITORY + "/compare/"
allowed = [prefix + SOURCE + "..." + RUNNER + "?per_page=100",
prefix + RUNNER + "...main?per_page=1", prefix + SOURCE + "...main"]
for path in allowed:
self.assertEqual(provider.api(path), {})
self.assertEqual([call[0] for call in commands.calls],
[["gh", "api", "--hostname", "github.com", "--method", "GET", path]
for path in allowed])
refused = [prefix + SOURCE + suffix for suffix in (
".." + RUNNER, "...." + RUNNER, "...main/../secrets", "...main?per_page=2",
"...main?per_page=100", "...main&per_page=1", "...main?per_page=1&page=2",
"..." + RUNNER, "..." + RUNNER + "?per_page=1", "...foreign?per_page=1")]
refused += [prefix + "main..." + RUNNER + "?per_page=100",
prefix + "A" * 40 + "...main", prefix + SOURCE + "...%2e%2e",
"repos/foreign/server/compare/" + SOURCE + "...main",
"repos/" + E.REPOSITORY + "/contents/../secrets"]
for path in refused:
with self.subTest(path=path), self.assertRaisesRegex(E.Refusal, "provider_api_path"):
provider.api(path)
self.assertEqual(len(commands.calls), len(allowed), "refused paths never reach the command transport")

def test_runner_ancestry_uses_the_actual_provider_path_guard(self):
fixture = RunnerProvider()

class ApiCommands:
def __init__(self):
self.paths = []

def run(self, argv, limit=E.JSON_LIMIT, env=None, accepted=(0,)):
self_test.assertEqual(argv[:6], ["gh", "api", "--hostname", "github.com", "--method", "GET"])
self_test.assertEqual(len(argv), 7)
self.paths.append(argv[6])
return E.canonical(fixture.api(argv[6]))

self_test = self
commands = ApiCommands()
provider = T.GitHubRegistryProvider(commands)
selected = fixture.selected
self.assertEqual(E.runner_subject(provider, selected["runner"], selected["release"]), fixture.binding)
self.assertEqual(commands.paths, [
f"repos/{E.REPOSITORY}/contents/{E.SUBJECT_PATH}?ref={RUNNER}",
f"repos/{E.REPOSITORY}/compare/{SOURCE}...{RUNNER}?per_page=100",
f"repos/{E.REPOSITORY}/compare/{RUNNER}...main?per_page=1",
])
fixture.on_main = False
with self.assertRaisesRegex(E.Refusal, "qualification_runner_not_on_main"):
E.runner_subject(provider, selected["runner"], selected["release"])
fixture.on_main = True
fixture.diff_files.append({"filename": "rust/src/runtime/store.rs", "status": "modified"})
with self.assertRaisesRegex(E.Refusal, "qualification_runner_frozen_source"):
E.runner_subject(provider, selected["runner"], selected["release"])

def test_unprivileged_plan_requires_exact_tag_and_accepted_main_source(self):
value = {"release": {"tag": "3.2.0", "source_commit": SOURCE}}
context = {**CONTEXT, "GITHUB_WORKFLOW_REF": E.REPOSITORY + "/.github/workflows/native-alias-promotion.yml@refs/tags/3.2.0"}
Expand Down
Loading