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
This commit is contained in:
Riccardo Schirone
2025-07-31 15:55:36 +02:00
committed by GitHub
parent 744a5ae2e6
commit 5cfa9bd26e
33 changed files with 412 additions and 72 deletions
+27
View File
@@ -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=<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..."
+3 -5
View File
@@ -120,10 +120,10 @@ buttercup-ui ClusterIP 10.106.49.166 <none> 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:**
+17 -4
View File
@@ -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"]
@@ -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(
+14 -1
View File
@@ -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"
+34 -3
View File
@@ -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",
+12 -1
View File
@@ -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 = [
@@ -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:
+1 -1
View File
@@ -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
+5 -1
View File
@@ -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 = [
+8
View File
@@ -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)
+2
View File
@@ -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)
+73
View File
@@ -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 <team-id>:<secret> -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 <team-id>:<secret> -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://<team-moniker>-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.
@@ -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",
@@ -2,9 +2,11 @@ definitions:
types.Architecture:
enum:
- x86_64
- aarch64
type: string
x-enum-varnames:
- ArchitectureX8664
- ArchitectureAArch64
types.Assessment:
enum:
- correct
+7
View File
@@ -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)
@@ -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.
+6 -7
View File
@@ -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
}'
+9
View File
@@ -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
}'
+2 -1
View File
@@ -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/"
@@ -1 +1 @@
7.14.0-SNAPSHOT
7.14.0
@@ -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:
@@ -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,
@@ -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
@@ -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):
+1
View File
@@ -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" },
+5 -1
View File
@@ -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" },
+1 -1
View File
@@ -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
+5 -1
View File
@@ -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 = [
+40 -25
View File
@@ -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
}
+1 -1
View File
@@ -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
+1
View File
@@ -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" },