From 5cfa9bd26eee53c0fde43b272731b7cae7bb39fd Mon Sep 17 00:00:00 2001 From: Riccardo Schirone <562321+ret2libc@users.noreply.github.com> Date: Thu, 31 Jul 2025 15:55:36 +0200 Subject: [PATCH] Add support for ARM (#207) * Fix integration-test run * use the proper oss-fuzz-aixcc commit to run integration-test * determine OSS_FUZZ_CONTAINER_ORG at runtime * use `git diff` in the patcher to create the patch to fix a problem with patches affecting non-newline terminated files * fix parsing * fix program-model lint * Modify CRS to work on MacOS/ARM * Use LibUCL for testing * use example-libpng * fix linting * common: fix tests --- Makefile | 27 +++++ README.md | 8 +- common/pyproject.toml | 21 +++- common/src/buttercup/common/challenge_task.py | 17 +-- common/src/buttercup/common/constants.py | 15 ++- common/tests/test_challenge_task.py | 37 +++++- common/uv.lock | 13 +- .../k8s/charts/program-model/values.yaml | 6 +- fuzzer/dockerfiles/runner_image.Dockerfile | 2 +- fuzzer/uv.lock | 6 +- orchestrator/docs/README.md | 8 ++ orchestrator/docs/TypesArchitecture.md | 2 + orchestrator/docs/api/README.md | 73 +++++++++++ .../docs/api/competition-swagger-v1.4.0.json | 4 +- .../docs/api/competition-swagger-v1.4.0.yaml | 2 + orchestrator/docs/round_info/README.md | 7 ++ .../docs/round_info/exhibition-round-3.md | 113 ++++++++++++++++++ orchestrator/scripts/task_crs.sh | 13 +- orchestrator/scripts/task_upstream_libucl.sh | 9 ++ orchestrator/scripts/update_apis.sh | 3 +- .../orchestrator/.openapi-generator/VERSION | 2 +- .../models/types_architecture.py | 1 + .../orchestrator/scheduler/submissions.py | 3 +- .../ui/competition_api/__init__.py | 0 .../ui/competition_api/models/__init__.py | 4 +- .../ui/competition_api/models/types.py | 5 +- orchestrator/uv.lock | 1 + patcher/uv.lock | 6 +- program-model/Dockerfile | 2 +- program-model/uv.lock | 6 +- scripts/common.sh | 65 ++++++---- scripts/setup-local.sh | 2 +- seed-gen/uv.lock | 1 + 33 files changed, 412 insertions(+), 72 deletions(-) create mode 100644 orchestrator/docs/round_info/exhibition-round-3.md create mode 100755 orchestrator/scripts/task_upstream_libucl.sh delete mode 100644 orchestrator/src/buttercup/orchestrator/ui/competition_api/__init__.py diff --git a/Makefile b/Makefile index 8067328a..a4119ca9 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ help: @echo "" @echo "Testing:" @echo " send-integration-task - Run integration-test task" + @echo " send-libpng-task - Run libpng task" @echo "" @echo "Development:" @echo " install-cscope - Install cscope tool" @@ -128,6 +129,17 @@ send-integration-task: ./orchestrator/scripts/task_integration_test.sh @pkill -f "kubectl port-forward" || true +send-libpng-task: + @echo "Running libpng task..." + @if ! kubectl get namespace $${BUTTERCUP_NAMESPACE:-crs} >/dev/null 2>&1; then \ + echo "Error: CRS namespace not found. Deploy first with 'make deploy'."; \ + exit 1; \ + fi + kubectl port-forward -n $${BUTTERCUP_NAMESPACE:-crs} service/buttercup-ui 31323:1323 & + @sleep 3 + ./orchestrator/scripts/task_crs.sh + @pkill -f "kubectl port-forward" || true + # Development targets lint: @echo "Linting all Python code..." @@ -148,6 +160,21 @@ lint-component: cd $(COMPONENT) && uv run mypy; \ fi +reformat: + @echo "Reformatting all Python code..." + @for component in common orchestrator fuzzer program-model seed-gen patcher; do \ + make --no-print-directory reformat-component COMPONENT=$$component; \ + done + +reformat-component: + @if [ -z "$(COMPONENT)" ]; then \ + echo "Error: COMPONENT not specified. Usage: make reformat-component COMPONENT="; \ + echo "Available components: common, fuzzer, orchestrator, patcher, program-model, seed-gen"; \ + exit 1; \ + fi + @echo "Reformatting $(COMPONENT)..." + @cd $(COMPONENT) && uv sync -q --all-extras && uv run ruff format && uv run ruff check --fix + # Cleanup targets undeploy: @echo "Cleaning up deployment..." diff --git a/README.md b/README.md index cd2bffe1..49caa04f 100644 --- a/README.md +++ b/README.md @@ -120,10 +120,10 @@ buttercup-ui ClusterIP 10.106.49.166 1323/TCP All CRS pods up and running. ``` -3. **Submit the integration-test challenge to the CRS (for 30mins):** +3. **Submit the libpng project to the CRS (for 30mins):** ```bash -make send-integration-task +make send-libpng-task ``` **Alternative manual commands:** @@ -265,6 +265,7 @@ make status # Check the status of the deployment # Testing make send-integration-task # Run integration test task +make send-libpng-task # Run libpng test task # Development make lint # Lint all Python code @@ -283,9 +284,6 @@ make lint # Lint specific component make lint-component COMPONENT=orchestrator - -# Run test task -make send-integration-task ``` **Alternative manual commands:** diff --git a/common/pyproject.toml b/common/pyproject.toml index dca1a780..64a8a73b 100644 --- a/common/pyproject.toml +++ b/common/pyproject.toml @@ -14,8 +14,8 @@ dependencies = [ "langfuse ~= 2.59.2", "langchain ~= 0.3.18", "six>=1.17.0", - "openlit == 1.32.12", # 1.33 currently fails to import opentelemetry.sdk._events - "langchain-community ~= 0.3.20", # implicit dep of openlit if you instrument langchain + "openlit == 1.32.12", # 1.33 currently fails to import opentelemetry.sdk._events + "langchain-community ~= 0.3.20", # implicit dep of openlit if you instrument langchain "langchain-text-splitters ~= 0.3.7", # implicit dep of openlit if you instrument langchain ] @@ -33,11 +33,24 @@ requires = ["hatchling"] build-backend = "hatchling.build" [dependency-groups] -dev = ["pytest>=8.3.4", "ruff>=0.9.2", "mypy>=1.15.0", "types-requests", "types-PyYAML", "types-redis"] +dev = [ + "pytest>=8.3.4", + "ruff>=0.9.2", + "mypy>=1.15.0", + "types-requests", + "types-PyYAML", + "types-redis", + "dirty-equals>=0.9.0", +] [tool.ruff] line-length = 120 -exclude = ["./src/buttercup/common/datastructures", "./src/buttercup/common/clusterfuzz_env", "./src/buttercup/common/clusterfuzz_parser", "src/buttercup/common/clusterfuzz_utils.py"] +exclude = [ + "./src/buttercup/common/datastructures", + "./src/buttercup/common/clusterfuzz_env", + "./src/buttercup/common/clusterfuzz_parser", + "src/buttercup/common/clusterfuzz_utils.py", +] [tool.mypy] plugins = ["pydantic.mypy"] diff --git a/common/src/buttercup/common/challenge_task.py b/common/src/buttercup/common/challenge_task.py index 350dfbff..13784e63 100644 --- a/common/src/buttercup/common/challenge_task.py +++ b/common/src/buttercup/common/challenge_task.py @@ -19,6 +19,7 @@ from buttercup.common.utils import copyanything, get_diffs from buttercup.common.stack_parsing import get_crash_token from typing import Iterator import buttercup.common.node_local as node_local +from buttercup.common.constants import ARCHITECTURE from packaging.version import Version logger = logging.getLogger(__name__) @@ -532,7 +533,7 @@ class ChallengeTask: *, pull_latest_base_image: bool = False, cache: bool | None = None, - architecture: str | None = None, + architecture: str | None = ARCHITECTURE, ) -> CommandResult: logger.info( "Building image for project %s | pull_latest_base_image=%s | cache=%s | architecture=%s", @@ -560,7 +561,7 @@ class ChallengeTask: self, use_source_dir: bool = True, *, - architecture: str | None = None, + architecture: str | None = ARCHITECTURE, engine: str | None = None, sanitizer: str | None = None, env: Dict[str, str] | None = None, @@ -605,7 +606,7 @@ class ChallengeTask: self, use_source_dir: bool = True, *, - architecture: str | None = None, + architecture: str | None = ARCHITECTURE, engine: str | None = None, sanitizer: str | None = None, pull_latest_base_image: bool = True, @@ -634,7 +635,7 @@ class ChallengeTask: container_name: str, use_source_dir: bool = True, *, - architecture: str | None = None, + architecture: str | None = ARCHITECTURE, engine: str | None = None, sanitizer: str | None = None, pull_latest_base_image: bool = True, @@ -658,7 +659,7 @@ class ChallengeTask: def check_build( self, *, - architecture: str | None = None, + architecture: str | None = ARCHITECTURE, engine: str | None = None, sanitizer: str | None = None, env: Dict[str, str] | None = None, @@ -689,7 +690,7 @@ class ChallengeTask: crash_path: Path, fuzzer_args: list[str] | None = None, *, - architecture: str | None = None, + architecture: str | None = ARCHITECTURE, env: Dict[str, str] | None = None, ) -> ReproduceResult: logger.info( @@ -735,7 +736,7 @@ class ChallengeTask: harness_name: str, fuzzer_args: list[str] | None = None, corpus_dir: Path | None = None, - architecture: str | None = None, + architecture: str | None = ARCHITECTURE, engine: str | None = None, sanitizer: str | None = None, env: Dict[str, str] | None = None, @@ -772,7 +773,7 @@ class ChallengeTask: self, harness_name: str, corpus_dir: str, - architecture: str | None = None, + architecture: str | None = ARCHITECTURE, env: Dict[str, str] | None = None, ) -> CommandResult: logger.info( diff --git a/common/src/buttercup/common/constants.py b/common/src/buttercup/common/constants.py index 2c8e3fb7..b8c7d02a 100644 --- a/common/src/buttercup/common/constants.py +++ b/common/src/buttercup/common/constants.py @@ -1,6 +1,19 @@ +import platform + + +def _detect_architecture() -> str: + machine = platform.machine().lower() + if machine in ("x86_64", "amd64"): + return "x86_64" + elif machine in ("aarch64", "arm64"): + return "aarch64" + else: + raise RuntimeError(f"Unsupported architecture: {machine}") + + CORPUS_DIR_NAME = "buttercup_corpus" CRASH_DIR_NAME = "buttercup_crashes" # This is fixed for every task, so we can hardcode it here -ARCHITECTURE = "x86_64" +ARCHITECTURE = _detect_architecture() ADDRESS_SANITIZER = "address" diff --git a/common/tests/test_challenge_task.py b/common/tests/test_challenge_task.py index 8897e389..6098e3d9 100644 --- a/common/tests/test_challenge_task.py +++ b/common/tests/test_challenge_task.py @@ -1,6 +1,7 @@ from pathlib import Path import pytest from unittest.mock import MagicMock, patch +from dirty_equals import IsStr import subprocess import os import base64 @@ -159,7 +160,15 @@ def test_build_image(challenge_task: ChallengeTask, mock_subprocess): # Verify the command and working directory args, kwargs = mock_subprocess.call_args - assert args[0] == ["python", "infra/helper.py", "build_image", "--no-pull", "example_project"] + assert args[0] == [ + "python", + "infra/helper.py", + "build_image", + "--no-pull", + "--architecture", + IsStr, + "example_project", + ] assert kwargs["cwd"] == challenge_task.task_dir / challenge_task.get_oss_fuzz_subpath() # Verify output was read @@ -181,6 +190,8 @@ def test_build_fuzzers(challenge_task: ChallengeTask, mock_subprocess): "python", "infra/helper.py", "build_fuzzers", + "--architecture", + IsStr, "--engine", "libfuzzer", "--sanitizer", @@ -202,6 +213,8 @@ def test_check_build(challenge_task: ChallengeTask, mock_subprocess): "python", "infra/helper.py", "check_build", + "--architecture", + IsStr, "--engine", "libfuzzer", "--sanitizer", @@ -222,7 +235,15 @@ def test_reproduce_pov(challenge_task: ChallengeTask, mock_subprocess): assert result.did_crash() is False mock_subprocess.assert_called_once() args, kwargs = mock_subprocess.call_args - assert args[0][:-1] == ["python", "infra/helper.py", "reproduce", "example_project", "fuzz_target"] + assert args[0][:-1] == [ + "python", + "infra/helper.py", + "reproduce", + "--architecture", + IsStr, + "example_project", + "fuzz_target", + ] assert args[0][-1].endswith("/crash-sample") assert kwargs["cwd"] == challenge_task.task_dir / challenge_task.get_oss_fuzz_subpath() @@ -270,7 +291,15 @@ def test_build_image_custom_python(challenge_task_custom_python: ChallengeTask, assert result.success is True mock_subprocess.assert_called_once() args, kwargs = mock_subprocess.call_args - assert args[0] == ["/usr/bin/python3", "infra/helper.py", "build_image", "--no-pull", "example_project"] + assert args[0] == [ + "/usr/bin/python3", + "infra/helper.py", + "build_image", + "--no-pull", + "--architecture", + IsStr, + "example_project", + ] assert kwargs["cwd"] == challenge_task_custom_python.task_dir / challenge_task_custom_python.get_oss_fuzz_subpath() @@ -288,6 +317,8 @@ def test_build_fuzzers_custom_python(challenge_task_custom_python: ChallengeTask "/usr/bin/python3", "infra/helper.py", "build_fuzzers", + "--architecture", + IsStr, "--engine", "libfuzzer", "--sanitizer", diff --git a/common/uv.lock b/common/uv.lock index f7b81d85..c78efb92 100644 --- a/common/uv.lock +++ b/common/uv.lock @@ -327,6 +327,7 @@ dependencies = [ [package.dev-dependencies] dev = [ + { name = "dirty-equals" }, { name = "mypy" }, { name = "pytest" }, { name = "ruff" }, @@ -353,6 +354,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ + { name = "dirty-equals", specifier = ">=0.9.0" }, { name = "mypy", specifier = ">=1.15.0" }, { name = "pytest", specifier = ">=8.3.4" }, { name = "ruff", specifier = ">=0.9.2" }, @@ -433,6 +435,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998, upload-time = "2025-01-27T10:46:09.186Z" }, ] +[[package]] +name = "dirty-equals" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/99/133892f401ced5a27e641a473c547d5fbdb39af8f85dac8a9d633ea3e7a7/dirty_equals-0.9.0.tar.gz", hash = "sha256:17f515970b04ed7900b733c95fd8091f4f85e52f1fb5f268757f25c858eb1f7b", size = 50412, upload-time = "2025-01-11T23:23:40.491Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/0c/03cc99bf3b6328604b10829de3460f2b2ad3373200c45665c38508e550c6/dirty_equals-0.9.0-py3-none-any.whl", hash = "sha256:ff4d027f5cfa1b69573af00f7ba9043ea652dbdce3fe5cbe828e478c7346db9c", size = 28226, upload-time = "2025-01-11T23:23:37.489Z" }, +] + [[package]] name = "distro" version = "1.9.0" @@ -456,7 +467,7 @@ name = "exceptiongroup" version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } wheels = [ diff --git a/deployment/k8s/charts/program-model/values.yaml b/deployment/k8s/charts/program-model/values.yaml index 90a550a1..096e9a77 100644 --- a/deployment/k8s/charts/program-model/values.yaml +++ b/deployment/k8s/charts/program-model/values.yaml @@ -2,11 +2,11 @@ enabled: true resources: limits: - cpu: 300m + cpu: 500m memory: 1Gi requests: - cpu: 100m - memory: 512Mi + cpu: 250m + memory: 256Mi dind: resources: diff --git a/fuzzer/dockerfiles/runner_image.Dockerfile b/fuzzer/dockerfiles/runner_image.Dockerfile index 756f788d..8bfa599b 100644 --- a/fuzzer/dockerfiles/runner_image.Dockerfile +++ b/fuzzer/dockerfiles/runner_image.Dockerfile @@ -13,7 +13,7 @@ RUN uv python install python3.10 FROM base-image AS runner-base RUN apt-get update # TODO(Ian): maybe we should have a different base image for the builder -RUN apt-get install ca-certificates curl +RUN apt-get install -y ca-certificates curl RUN install -m 0755 -d /etc/apt/keyrings RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc RUN chmod a+r /etc/apt/keyrings/docker.asc diff --git a/fuzzer/uv.lock b/fuzzer/uv.lock index 57e045df..6200aed5 100644 --- a/fuzzer/uv.lock +++ b/fuzzer/uv.lock @@ -395,9 +395,13 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ + { name = "dirty-equals", specifier = ">=0.9.0" }, { name = "mypy", specifier = ">=1.15.0" }, { name = "pytest", specifier = ">=8.3.4" }, { name = "ruff", specifier = ">=0.9.2" }, + { name = "types-pyyaml" }, + { name = "types-redis" }, + { name = "types-requests" }, ] [[package]] @@ -448,7 +452,7 @@ name = "exceptiongroup" version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } wheels = [ diff --git a/orchestrator/docs/README.md b/orchestrator/docs/README.md index b91fdfcb..f49b50c9 100644 --- a/orchestrator/docs/README.md +++ b/orchestrator/docs/README.md @@ -51,8 +51,16 @@ Welcome to the documentation page for the AIxCC Final Competition. - [Challenge Sequencing](round_info/exhibition-round-1.md#challenge-sequencing) - [Exhibition Round 2](round_info/exhibition-round-2.md#exhibition-round-2) + - [Repositories](round_info/exhibition-round-2.md#repositories) + - [Challenge Tasks Archive](round_info/exhibition-round-2.md#challenge-tasks-archive) - [Sequencing](round_info/exhibition-round-2.md#sequencing) - [Notes](round_info/exhibition-round-2.md#notes) + - [Exhibition Round 3](round_info/exhibition-round-3.md#exhibition-round-3) + - [Repositories](round_info/exhibition-round-3.md#repositories) + - [Challenge Tasks Archive](round_info/exhibition-round-3.md#challenge-tasks-archive) + - [Sequencing](round_info/exhibition-round-3.md#sequencing) + - [Notes](round_info/exhibition-round-3.md#notes) + - [LLM Models](llm-models/README.md#llm-providers-and-models) diff --git a/orchestrator/docs/TypesArchitecture.md b/orchestrator/docs/TypesArchitecture.md index c8887e75..b587011f 100644 --- a/orchestrator/docs/TypesArchitecture.md +++ b/orchestrator/docs/TypesArchitecture.md @@ -5,6 +5,8 @@ * `ArchitectureX8664` (value: `'x86_64'`) +* `ArchitectureAArch64` (value: `'aarch64'`) + [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/orchestrator/docs/api/README.md b/orchestrator/docs/api/README.md index 5de4ce49..2561c611 100644 --- a/orchestrator/docs/api/README.md +++ b/orchestrator/docs/api/README.md @@ -94,6 +94,79 @@ Here is an example curl you may use to trigger the integration testing task. curl -u 11111111-1111-1111-1111-111111111111:pY8rLk7FvQ2hZm9GwUx3Ej5BnTcV4So0 -X 'POST' 'https://api.aixcc.tech/v1/request/delta/' -H 'Content-Type: application/json' -d '{"duration_secs": 3600 }' ``` +### Request Arbitrary Exhibition 3 Task + +We have updated the production Competition API /v1/request/ endpoint to include all exhibition3 challenges. Exercising this endpoint should look the same as exercising the /v1/request/delta/ endpoint, but +instead of putting delta in the URL path, you can input the challenge name. + +For example: + +```bash +curl -u : -X 'POST' 'https://api.aixcc.tech/v1/request/ex3-tk-full-01/' --json '{"duration_secs":43200}' +``` + +You may, of course, still use /v1/request/delta/, and you will get the same integration test challenge as you would have before Exhibition 3. In order to get a list of the challenges that are available, you may use +the /v1/request/list/ endpoint: + +```bash +curl -u : -X 'GET' 'https://api.aixcc.tech/v1/request/list/' +``` + +Some teams have noted that the hosted competitor test servers were having issues scaling to respond to the number of submissions. Unfortunately, this is a limit. The official competition API, however, is built to +handle hundreds of POVs at the same time. So if you are running into any issues with the competitor test server, we highly recommend you use this new endpoint. We will still also still be updating the competitor test +servers as we have been before when updates are made to evaluation scripts. + +Use the following URL/hostname configurations in order to use this new requesting feature: + +**CRS API URL**: `https://-final.tail7e9b4c.ts.net` + +**Competition API URL**: `https://api.tail7e9b4c.ts.net` + +Here's a reference for which challenge names correspond to which repos: + +- **Apache Commons Compress** + - ex3-cc-delta-02 + - ex3-cc-delta-03 + - ex3-cc-full-01 +- **FreeRDP** + - ex3-fp-delta-01 + - ex3-fp-full-01 +- **Integration Test** (There should be no significant differences between "ex3-integration-test-delta-01" and "delta") + - ex3-integration-test-delta-01 + - ex3-integration-test-unharnessed-delta-01 + - delta +- **libpng** + - ex3-lp-delta-01 +- **libxml2** + - ex3-lx-delta-01 + - ex3-lx-delta-02 +- **sqlite3** + - ex3-sq-delta-01 + - ex3-sq-delta-02 + - ex3-sq-delta-03 + - ex3-sq-full-01 +- **Apache Tika** + - ex3-tk-full-01 + - ex3-tk-delta-02 + - ex3-tk-delta-03 + - ex3-tk-delta-04 + - ex3-tk-delta-05 +- **Apache Zookeeper** + - ex3-zk-delta-01 + - ex3-zk-delta-02 + - ex3-zk-full-01 +- **Curl** + - ex3-cu-full-01 + - ex3-cu-delta-01 +- **libexif** + - ex3-ex-delta-01 +- **libpostal** + - ex3-libpostal-full-01 +- **s2n-tls** + - ex3-s2n_tls-full-01 +- **IPF** + - ex3-ipf-full-01 + ## CRS API Task Statuses The CRS API has a status endpoint which provides a summary of tasks by status, among other things. The statuses which tasks go through are documented in the state diagram below. diff --git a/orchestrator/docs/api/competition-swagger-v1.4.0.json b/orchestrator/docs/api/competition-swagger-v1.4.0.json index 88488804..73611d71 100644 --- a/orchestrator/docs/api/competition-swagger-v1.4.0.json +++ b/orchestrator/docs/api/competition-swagger-v1.4.0.json @@ -864,8 +864,8 @@ "definitions": { "types.Architecture": { "type": "string", - "enum": ["x86_64"], - "x-enum-varnames": ["ArchitectureX8664"] + "enum": ["x86_64", "aarch64"], + "x-enum-varnames": ["ArchitectureX8664", "ArchitectureAArch64"] }, "types.Assessment": { "type": "string", diff --git a/orchestrator/docs/api/competition-swagger-v1.4.0.yaml b/orchestrator/docs/api/competition-swagger-v1.4.0.yaml index 7e03572b..4b50b91a 100644 --- a/orchestrator/docs/api/competition-swagger-v1.4.0.yaml +++ b/orchestrator/docs/api/competition-swagger-v1.4.0.yaml @@ -2,9 +2,11 @@ definitions: types.Architecture: enum: - x86_64 + - aarch64 type: string x-enum-varnames: - ArchitectureX8664 + - ArchitectureAArch64 types.Assessment: enum: - correct diff --git a/orchestrator/docs/round_info/README.md b/orchestrator/docs/round_info/README.md index 00fb7caa..e683c01b 100644 --- a/orchestrator/docs/round_info/README.md +++ b/orchestrator/docs/round_info/README.md @@ -13,3 +13,10 @@ See more at [Exhibition Round 1](./exhibition-round-1.md) 9 Delta Challenges (6 C, 3 Java), 6 Full Scan Challenges (4 C, 2 Java). See more at [Exhibition Round 2](./exhibition-round-2.md) + +## Exhibition 3 + +19 Delta Challenges (10 C, 9 Java), 8 Full Scan Challenges (5 C, 3 Java), +1 Unharnessed Delta Challenege (C), 2 Unharnessed Full Scan Challenges (1 C, 1 Java). + +See more at [Exhibition Round 3](./exhibition-round-3.md) diff --git a/orchestrator/docs/round_info/exhibition-round-3.md b/orchestrator/docs/round_info/exhibition-round-3.md new file mode 100644 index 00000000..6d5132cb --- /dev/null +++ b/orchestrator/docs/round_info/exhibition-round-3.md @@ -0,0 +1,113 @@ +# Exhibition Round 3 + +19 Delta Challenges (10 C, 9 Java), 8 Full Scan Challenges (5 C, 3 Java), 1 Unharnessed Delta Challenege (C), 2 Unharnessed Full Scan Challenges (1 C, 1 Java). + +## Repositories + +The following repositories were used during the Round: + +- Libxml2 (C) +- Libpng (C) +- AIxCC Integration Test (C) +- FreeRDP (C) +- Sqlite (C) +- Dropbear (C) +- Curl (C) +- Libexif (C) +- Libpostal (C) +- s2n-tls +- Tika (Java) +- Zookeeper (Java) +- Apache Commons Compress (Java) +- IPF (Java) + +## Challenge Tasks Archive + +Download all challenge tasks that were sent during the Round: [exhibition3_challenge_tasks.tar.gz](./exhibition3_challenge_tasks.tar.gz). _See notes below_. + +Note: Times included in tasks.json are descriptive of when the Competition API generated the task, +and not the exact times of when the message was broadcast to each CRS (it should be close though!). + +## Sequencing + +```mermaid +flowchart TD + subgraph A["Challenge Set #1 (12 hr)"] + direction LR + A0["freerdp: Full 1"] + B0["sqlite: Full 1"] + C0["dropbear: Full 1"] + D0["libpostal: Full 1"] + E0["curl: Full 1"] + + end + + subgraph B["Challenge Set #2 (12 hr)"] + direction LR + A1["commons-compress: Full 1"] + B1["zookeeper: Full 1"] + C1["tika: Full 1"] + end + + subgraph C["Challenge Set #3 (6 hr)"] + direction LR + A2["freerdp: Delta 1"] + B2["integration-test: Delta 1"] + C2["libpng: Delta 1"] + F2["curl: Delta 1"] + E2["libexif: Delta 1"] + end + + subgraph D["Challenge Set #4 (6 hr)"] + direction LR + A3["sqlite: Delta 1"] + B3["sqlite: Delta 2"] + C3["sqlite: Delta 3"] + D3["libxml2: Delta 1"] + E3["libxml2: Delta 2"] + end + + subgraph E["Challenge Set #5 (6 hr)"] + direction LR + A4["tika: Delta 1"] + B4["tika: Delta 2"] + C4["tika: Delta 3"] + D4["tika: Delta 4"] + E4["tika: Delta 5"] + end + + subgraph F["Challenge Set #6 (6 hr)"] + direction LR + A5["zookeeper: Delta 1"] + B5["zookeeper: Delta 2"] + C5["commons-compress: Delta 2"] + D5["commons-compress: Delta 3"] + end + + subgraph G["Challenge Set #7 (12 hr)"] + direction LR + A6["ipf (Unharnessed): Full 1"] + B6["s2n-tls (Unharnessed): Full 1"] + end + + subgraph H["Challenge Set #8 (6 hr)"] + direction LR + A7["integration-test (Unharnessed): Delta 1"] + end + + A --> B --> C --> D --> E --> F --> G --> H +``` + +## Notes + +\* There were three challenges that, as sent to competitors, would have prevented meaningful POV and Patch submissions. These challenges were a part of the Tika challenge repository. +The challenges are: + +- tk-delta-01 (task_id: `01974bc3-f9ed-73a2-9c76-bfe9d5311d3d`) +- tk-delta-03 (task_id: `01974bd4-0df0-7d96-aad6-4e3db89d190d`) +- tk-delta-05 (task_id: `01974bd4-10e9-784a-b6c7-2c5dc5151cf6`) + +All of these challenges had diffs that would fail to apply. +For tk-delta-03 and tk-delta-05, they would fail to apply because of binary contents within the diff. +For tk-delta-01, the issue is related to inconsistent filenames in diff headers. We have fixed the root causes of the issues for these three challenges. +These Competition API fixes are now available via the new request/challenges endpoint and competitor-test-api. diff --git a/orchestrator/scripts/task_crs.sh b/orchestrator/scripts/task_crs.sh index 630639ad..d868f0c9 100755 --- a/orchestrator/scripts/task_crs.sh +++ b/orchestrator/scripts/task_crs.sh @@ -1,11 +1,10 @@ #!/bin/bash curl -X 'POST' 'http://127.0.0.1:31323/webhook/trigger_task' -H 'Content-Type: application/json' -d '{ - "challenge_repo_url": "git@github.com:tob-challenges/example-libpng.git", - "challenge_repo_base_ref": "0cc367aaeaac3f888f255cee5d394968996f736e", - "challenge_repo_head_ref": "fdacd5a1dcff42175117d674b0fda9f8a005ae88", - "fuzz_tooling_url": "git@github.com:tob-challenges/oss-fuzz-aixcc.git", - "fuzz_tooling_ref": "d5fbd68fca66e6fa4f05899170d24e572b01853d", + "challenge_repo_url": "https://github.com/tob-challenges/example-libpng", + "challenge_repo_base_ref": "5bf8da2d7953974e5dfbd778429c3affd461f51a", + "challenge_repo_head_ref": "challenges/lp-delta-01", + "fuzz_tooling_url": "https://github.com/google/oss-fuzz", + "fuzz_tooling_ref": "master", "fuzz_tooling_project_name": "libpng", - "harnesses_included": true, - "duration": 7200 + "duration": 1800 }' diff --git a/orchestrator/scripts/task_upstream_libucl.sh b/orchestrator/scripts/task_upstream_libucl.sh new file mode 100755 index 00000000..12f76ec4 --- /dev/null +++ b/orchestrator/scripts/task_upstream_libucl.sh @@ -0,0 +1,9 @@ +#!/bin/bash +curl -X 'POST' 'http://127.0.0.1:31323/webhook/trigger_task' -H 'Content-Type: application/json' -d '{ + "challenge_repo_url": "https://github.com/vstakhov/libucl", + "challenge_repo_head_ref": "8a0294f9eaa4e70342e562cb92792bbe3df90e70", + "fuzz_tooling_url": "https://github.com/google/oss-fuzz", + "fuzz_tooling_ref": "master", + "fuzz_tooling_project_name": "libucl", + "duration": 1800 +}' diff --git a/orchestrator/scripts/update_apis.sh b/orchestrator/scripts/update_apis.sh index ad4d6de8..534ca6e1 100755 --- a/orchestrator/scripts/update_apis.sh +++ b/orchestrator/scripts/update_apis.sh @@ -22,7 +22,8 @@ PROJECT_DIR=$(dirname "$SCRIPT_DIR") cd "$PROJECT_DIR" || exit 1 -git clone git@github.com:tob-challenges/example-crs-architecture.git || true +# Clone the repo, but ignore LFS files to avoid downloading large files +GIT_LFS_SKIP_SMUDGE=1 git clone git@github.com:tob-challenges/example-crs-architecture.git || true git -C example-crs-architecture pull cp -rv example-crs-architecture/docs/ "$OUTPUT_DIR/src/buttercup/orchestrator/docs/" diff --git a/orchestrator/src/buttercup/orchestrator/.openapi-generator/VERSION b/orchestrator/src/buttercup/orchestrator/.openapi-generator/VERSION index 4c631cf2..e465da43 100644 --- a/orchestrator/src/buttercup/orchestrator/.openapi-generator/VERSION +++ b/orchestrator/src/buttercup/orchestrator/.openapi-generator/VERSION @@ -1 +1 @@ -7.14.0-SNAPSHOT +7.14.0 diff --git a/orchestrator/src/buttercup/orchestrator/competition_api_client/models/types_architecture.py b/orchestrator/src/buttercup/orchestrator/competition_api_client/models/types_architecture.py index 98bd6d36..44e9fd6c 100644 --- a/orchestrator/src/buttercup/orchestrator/competition_api_client/models/types_architecture.py +++ b/orchestrator/src/buttercup/orchestrator/competition_api_client/models/types_architecture.py @@ -27,6 +27,7 @@ class TypesArchitecture(str, Enum): allowed enum values """ ArchitectureX8664 = 'x86_64' + ArchitectureAArch64 = 'aarch64' @classmethod def from_json(cls, json_str: str) -> Self: diff --git a/orchestrator/src/buttercup/orchestrator/scheduler/submissions.py b/orchestrator/src/buttercup/orchestrator/scheduler/submissions.py index 1f76fad5..3c3d79c3 100644 --- a/orchestrator/src/buttercup/orchestrator/scheduler/submissions.py +++ b/orchestrator/src/buttercup/orchestrator/scheduler/submissions.py @@ -9,6 +9,7 @@ from opentelemetry import trace from opentelemetry.trace import Status, StatusCode import buttercup.common.node_local as node_local from pathlib import Path +from buttercup.common.constants import ARCHITECTURE from buttercup.common.queues import ReliableQueue, QueueFactory, QueueNames from buttercup.common.sets import PoVReproduceStatus from buttercup.common.datastructures.msg_pb2 import ( @@ -269,7 +270,7 @@ class CompetitionAPI: # Create submission payload from crash data submission = TypesPOVSubmission( - architecture=TypesArchitecture.ArchitectureX8664, + architecture=TypesArchitecture(ARCHITECTURE), engine=crash.crash.target.engine, fuzzer_name=crash.crash.harness_name, sanitizer=crash.crash.target.sanitizer, diff --git a/orchestrator/src/buttercup/orchestrator/ui/competition_api/__init__.py b/orchestrator/src/buttercup/orchestrator/ui/competition_api/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/orchestrator/src/buttercup/orchestrator/ui/competition_api/models/__init__.py b/orchestrator/src/buttercup/orchestrator/ui/competition_api/models/__init__.py index 9d575496..724096a6 100644 --- a/orchestrator/src/buttercup/orchestrator/ui/competition_api/models/__init__.py +++ b/orchestrator/src/buttercup/orchestrator/ui/competition_api/models/__init__.py @@ -1,3 +1,3 @@ # generated by fastapi-codegen: -# filename: /tmp/tmp.VWuWL8tFKl/openapi.json -# timestamp: 2025-07-08T08:17:32+00:00 +# filename: /var/folders/ck/j9600dhj1t3f1901qw9vs2bh0000gn/T/tmp.YsQ74ZHmdF/openapi.json +# timestamp: 2025-07-30T12:54:58+00:00 diff --git a/orchestrator/src/buttercup/orchestrator/ui/competition_api/models/types.py b/orchestrator/src/buttercup/orchestrator/ui/competition_api/models/types.py index abdc9e37..a3de59fa 100644 --- a/orchestrator/src/buttercup/orchestrator/ui/competition_api/models/types.py +++ b/orchestrator/src/buttercup/orchestrator/ui/competition_api/models/types.py @@ -1,6 +1,6 @@ # generated by fastapi-codegen: -# filename: /tmp/tmp.VWuWL8tFKl/openapi.json -# timestamp: 2025-07-08T08:17:32+00:00 +# filename: /var/folders/ck/j9600dhj1t3f1901qw9vs2bh0000gn/T/tmp.YsQ74ZHmdF/openapi.json +# timestamp: 2025-07-30T12:54:58+00:00 from __future__ import annotations @@ -12,6 +12,7 @@ from pydantic import BaseModel, Field, constr class Architecture(Enum): ArchitectureX8664 = "x86_64" + ArchitectureAArch64 = "aarch64" class Assessment(Enum): diff --git a/orchestrator/uv.lock b/orchestrator/uv.lock index ba1183eb..607be93c 100644 --- a/orchestrator/uv.lock +++ b/orchestrator/uv.lock @@ -289,6 +289,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ + { name = "dirty-equals", specifier = ">=0.9.0" }, { name = "mypy", specifier = ">=1.15.0" }, { name = "pytest", specifier = ">=8.3.4" }, { name = "ruff", specifier = ">=0.9.2" }, diff --git a/patcher/uv.lock b/patcher/uv.lock index 4efecc2a..46459fac 100644 --- a/patcher/uv.lock +++ b/patcher/uv.lock @@ -244,9 +244,13 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ + { name = "dirty-equals", specifier = ">=0.9.0" }, { name = "mypy", specifier = ">=1.15.0" }, { name = "pytest", specifier = ">=8.3.4" }, { name = "ruff", specifier = ">=0.9.2" }, + { name = "types-pyyaml" }, + { name = "types-redis" }, + { name = "types-requests" }, ] [[package]] @@ -1113,7 +1117,7 @@ requires-dist = [ { name = "flake8", marker = "extra == 'tests'", specifier = ">=7.1.1" }, { name = "langchain", specifier = "~=0.3.20" }, { name = "langchain-community", specifier = ">=0.3.16" }, - { name = "langgraph", specifier = "==0.5.2" }, + { name = "langgraph", specifier = "~=0.5.2" }, { name = "langgraph-checkpoint", specifier = ">=2.0.25" }, { name = "mypy", marker = "extra == 'tests'", specifier = ">=1.15.0" }, { name = "openai", specifier = ">=1.60.2" }, diff --git a/program-model/Dockerfile b/program-model/Dockerfile index ddeadc51..44afc92e 100644 --- a/program-model/Dockerfile +++ b/program-model/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ubuntu:24.04@sha256:f8b860e4f9036f2694571770da292642eebcc4c2ea0c70a1a9244c2a1d436cd9 +ARG BASE_IMAGE=ubuntu:24.04 FROM $BASE_IMAGE AS base RUN apt-get update && apt-get install -y software-properties-common diff --git a/program-model/uv.lock b/program-model/uv.lock index 3474395c..49411217 100644 --- a/program-model/uv.lock +++ b/program-model/uv.lock @@ -343,9 +343,13 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ + { name = "dirty-equals", specifier = ">=0.9.0" }, { name = "mypy", specifier = ">=1.15.0" }, { name = "pytest", specifier = ">=8.3.4" }, { name = "ruff", specifier = ">=0.9.2" }, + { name = "types-pyyaml" }, + { name = "types-redis" }, + { name = "types-requests" }, ] [[package]] @@ -396,7 +400,7 @@ name = "exceptiongroup" version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } wheels = [ diff --git a/scripts/common.sh b/scripts/common.sh index b4b4b974..9e1f3417 100755 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -44,6 +44,21 @@ check_not_root() { fi } +# Portable sed in-place editing function +# Usage: portable_sed "pattern" "file" +portable_sed() { + local pattern="$1" + local file="$2" + + if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS requires empty string after -i + sed -i '' "$pattern" "$file" + else + # Linux doesn't accept backup extension + sed -i "$pattern" "$file" + fi +} + # Function to install Docker install_docker() { print_status "Installing Docker..." @@ -272,7 +287,7 @@ read_and_set_config() { # Only update if value is not empty if [ -n "$value" ]; then - sed -i "s|.*export $var_name=.*|export $var_name=\"$value\"|" deployment/env + portable_sed "s|.*export $var_name=.*|export $var_name=\"$value\"|" deployment/env fi return 0 } @@ -286,11 +301,11 @@ configure_ghcr_optional() { # Compute GHCR_AUTH ghcr_auth=$(echo -n "$ghcr_username:$ghcr_pat" | base64 --wrap=0) - sed -i "s|.*export GHCR_AUTH=.*|export GHCR_AUTH=\"$ghcr_auth\"|" deployment/env + portable_sed "s|.*export GHCR_AUTH=.*|export GHCR_AUTH=\"$ghcr_auth\"|" deployment/env return 0 else # Clear GHCR_AUTH if skipped - sed -i "s|.*export GHCR_AUTH=.*|export GHCR_AUTH=\"\"|" deployment/env + portable_sed "s|.*export GHCR_AUTH=.*|export GHCR_AUTH=\"\"|" deployment/env return 1 fi } @@ -303,8 +318,8 @@ configure_docker_hub() { echo # Set Docker credentials (handles both commented and uncommented lines) - sed -i "s|.*export DOCKER_USERNAME=.*|export DOCKER_USERNAME=\"$docker_username\"|" deployment/env - sed -i "s|.*export DOCKER_PAT=.*|export DOCKER_PAT=\"$docker_pat\"|" deployment/env + portable_sed "s|.*export DOCKER_USERNAME=.*|export DOCKER_USERNAME=\"$docker_username\"|" deployment/env + portable_sed "s|.*export DOCKER_PAT=.*|export DOCKER_PAT=\"$docker_pat\"|" deployment/env return 0 fi return 1 @@ -365,11 +380,11 @@ configure_simple_api_key() { fi if [ -n "$value" ]; then - sed -i "s|.*export $var_name=.*|export $var_name=\"$value\"|" deployment/env + portable_sed "s|.*export $var_name=.*|export $var_name=\"$value\"|" deployment/env return 0 else # Clear the key if skipped (set to empty string) - sed -i "s|.*export $var_name=.*|export $var_name=\"\"|" deployment/env + portable_sed "s|.*export $var_name=.*|export $var_name=\"\"|" deployment/env return 1 fi } @@ -384,13 +399,13 @@ configure_docker_hub_optional() { echo # Set Docker credentials - sed -i "s|.*export DOCKER_USERNAME=.*|export DOCKER_USERNAME=\"$docker_username\"|" deployment/env - sed -i "s|.*export DOCKER_PAT=.*|export DOCKER_PAT=\"$docker_pat\"|" deployment/env + portable_sed "s|.*export DOCKER_USERNAME=.*|export DOCKER_USERNAME=\"$docker_username\"|" deployment/env + portable_sed "s|.*export DOCKER_PAT=.*|export DOCKER_PAT=\"$docker_pat\"|" deployment/env return 0 else # Clear Docker credentials if skipped - sed -i "s|.*export DOCKER_USERNAME=.*|export DOCKER_USERNAME=\"\"|" deployment/env - sed -i "s|.*export DOCKER_PAT=.*|export DOCKER_PAT=\"\"|" deployment/env + portable_sed "s|.*export DOCKER_USERNAME=.*|export DOCKER_USERNAME=\"\"|" deployment/env + portable_sed "s|.*export DOCKER_PAT=.*|export DOCKER_PAT=\"\"|" deployment/env return 1 fi } @@ -403,18 +418,18 @@ configure_otel_wrapper() { echo # Update the env file - sed -i "s|.*export OTEL_ENDPOINT=.*|export OTEL_ENDPOINT=\"$otel_endpoint\"|" deployment/env - sed -i "s|.*export OTEL_PROTOCOL=.*|export OTEL_PROTOCOL=\"$otel_protocol\"|" deployment/env + portable_sed "s|.*export OTEL_ENDPOINT=.*|export OTEL_ENDPOINT=\"$otel_endpoint\"|" deployment/env + portable_sed "s|.*export OTEL_PROTOCOL=.*|export OTEL_PROTOCOL=\"$otel_protocol\"|" deployment/env if [ -n "$otel_token" ]; then - sed -i "s|.*export OTEL_TOKEN=.*|export OTEL_TOKEN=\"$otel_token\"|" deployment/env + portable_sed "s|.*export OTEL_TOKEN=.*|export OTEL_TOKEN=\"$otel_token\"|" deployment/env fi return 0 else # Disable OTEL - sed -i "s|.*export OTEL_ENDPOINT=.*|# export OTEL_ENDPOINT=\"\"|" deployment/env - sed -i "s|.*export OTEL_PROTOCOL=.*|# export OTEL_PROTOCOL=\"http\"|" deployment/env - sed -i "s|.*export OTEL_TOKEN=.*|# export OTEL_TOKEN=\"\"|" deployment/env + portable_sed "s|.*export OTEL_ENDPOINT=.*|# export OTEL_ENDPOINT=\"\"|" deployment/env + portable_sed "s|.*export OTEL_PROTOCOL=.*|# export OTEL_PROTOCOL=\"http\"|" deployment/env + portable_sed "s|.*export OTEL_TOKEN=.*|# export OTEL_TOKEN=\"\"|" deployment/env return 1 fi } @@ -427,17 +442,17 @@ configure_langfuse_wrapper() { echo # Update the env file - sed -i "s|.*export LANGFUSE_ENABLED=.*|export LANGFUSE_ENABLED=true|" deployment/env - sed -i "s|.*export LANGFUSE_HOST=.*|export LANGFUSE_HOST=\"$langfuse_host\"|" deployment/env - sed -i "s|.*export LANGFUSE_PUBLIC_KEY=.*|export LANGFUSE_PUBLIC_KEY=\"$langfuse_public_key\"|" deployment/env - sed -i "s|.*export LANGFUSE_SECRET_KEY=.*|export LANGFUSE_SECRET_KEY=\"$langfuse_secret_key\"|" deployment/env + portable_sed "s|.*export LANGFUSE_ENABLED=.*|export LANGFUSE_ENABLED=true|" deployment/env + portable_sed "s|.*export LANGFUSE_HOST=.*|export LANGFUSE_HOST=\"$langfuse_host\"|" deployment/env + portable_sed "s|.*export LANGFUSE_PUBLIC_KEY=.*|export LANGFUSE_PUBLIC_KEY=\"$langfuse_public_key\"|" deployment/env + portable_sed "s|.*export LANGFUSE_SECRET_KEY=.*|export LANGFUSE_SECRET_KEY=\"$langfuse_secret_key\"|" deployment/env return 0 else # Disable and clear LangFuse configuration - sed -i "s|.*export LANGFUSE_ENABLED=.*|export LANGFUSE_ENABLED=false|" deployment/env - sed -i "s|.*export LANGFUSE_HOST=.*|export LANGFUSE_HOST=\"\"|" deployment/env - sed -i "s|.*export LANGFUSE_PUBLIC_KEY=.*|export LANGFUSE_PUBLIC_KEY=\"\"|" deployment/env - sed -i "s|.*export LANGFUSE_SECRET_KEY=.*|export LANGFUSE_SECRET_KEY=\"\"|" deployment/env + portable_sed "s|.*export LANGFUSE_ENABLED=.*|export LANGFUSE_ENABLED=false|" deployment/env + portable_sed "s|.*export LANGFUSE_HOST=.*|export LANGFUSE_HOST=\"\"|" deployment/env + portable_sed "s|.*export LANGFUSE_PUBLIC_KEY=.*|export LANGFUSE_PUBLIC_KEY=\"\"|" deployment/env + portable_sed "s|.*export LANGFUSE_SECRET_KEY=.*|export LANGFUSE_SECRET_KEY=\"\"|" deployment/env return 1 fi } diff --git a/scripts/setup-local.sh b/scripts/setup-local.sh index 25e77298..b229718f 100755 --- a/scripts/setup-local.sh +++ b/scripts/setup-local.sh @@ -37,7 +37,7 @@ verify_setup() { print_success "Setup verification completed successfully!" print_status "Next steps:" echo " 1. Run: make deploy-local" - echo " 2. Test with: make send-integration-task" + echo " 2. Test with: make send-libpng-task" else print_error "Setup verification failed. Run 'make validate' for details." exit 1 diff --git a/seed-gen/uv.lock b/seed-gen/uv.lock index 4d8b7afc..bda24859 100644 --- a/seed-gen/uv.lock +++ b/seed-gen/uv.lock @@ -244,6 +244,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ + { name = "dirty-equals", specifier = ">=0.9.0" }, { name = "mypy", specifier = ">=1.15.0" }, { name = "pytest", specifier = ">=8.3.4" }, { name = "ruff", specifier = ">=0.9.2" },