Skip to content

Commit 7a980be

Browse files
committed
feat: add support for running run, artifacts, and archive subcommands without fuzzing harness
Signed-off-by: Brandon Luo <br32710@ll.mit.edu>
1 parent 0061473 commit 7a980be

27 files changed

Lines changed: 1454 additions & 158 deletions

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ stricter subset of Keep a Changelog).
88

99
### Changed
1010

11+
- Auditing CRS type: does not require a target harness and produces bug-candidates
1112
- `oss-crs export` and `oss-crs import` commands — imports/exports docker images/CRS source code to transfer to another host.
1213
- Run-phase modules now default to `target_dependent: true`, so their images are built once per target during `build-target`. Set `target_dependent: false` for modules that can be built once during `prepare`.
1314
- `--offline` flag for all subcommands: disables git fetch

‎docs/config/crs.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,8 @@ The `supported_target` section defines what types of targets the CRS can work wi
231231
|-------|------|----------|-------------|
232232
| `mode` | `Set[TargetMode]` | Yes | Supported target modes (see [TargetMode](#targetmode)) |
233233
| `language` | `Set[TargetLanguage]` | Yes | Supported programming languages (see [TargetLanguage](#targetlanguage)) |
234-
| `sanitizer` | `Set[TargetSanitizer]` | Yes | Supported sanitizers (see [TargetSanitizer](#targetsanitizer)) |
235-
| `architecture` | `Set[TargetArch]` | Yes | Supported CPU architectures (see [TargetArch](#targetarch)) |
234+
| `sanitizer` | `Set[TargetSanitizer]` | No | Supported sanitizers (see [TargetSanitizer](#targetsanitizer)). Defaults to all sanitizers when omitted. |
235+
| `architecture` | `Set[TargetArch]` | No | Supported CPU architectures (see [TargetArch](#targetarch)). Defaults to all architectures when omitted. |
236236
| `fuzzing_engine` | `Set[FuzzingEngine]` | No | Supported fuzzing engines (see [FuzzingEngine](#fuzzingengine)). Defaults to all engines when omitted. |
237237

238238
### Example
@@ -271,6 +271,7 @@ Defines the type of CRS:
271271
| `bug-fixing-ensemble` | Ensemble CRS that aggregates bug-fixing outputs |
272272
| `bug-finding-triage` | Post-processor CRS that triages bug-finding results (e.g. deduplication, validation). Reads from the main exchange dir and writes to the processed exchange dir. |
273273
| `seed-filter` | Post-processor CRS that filters or prioritizes seeds/inputs for downstream CRS. Reads from the main exchange dir and writes to the processed exchange dir. |
274+
| `auditing` | Source-auditing CRS that analyzes `OSS_CRS_TARGET_SOURCE` and submits `bug-candidate` artifacts. It may run with or without a target harness. |
274275

275276
### TargetMode
276277

‎docs/config/target-project.md‎

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ Full spec (for OSS-Fuzz compatibility/reference): [OSS-Fuzz project.yaml referen
3333

3434
## Usage
3535

36+
### Harnessed Run
37+
3638
```bash
3739
# Build target
3840
uv run oss-crs build-target \
@@ -46,29 +48,35 @@ uv run oss-crs run \
4648
--target-harness <harness_name>
4749
```
4850

49-
| Argument | Required | Description |
50-
|-----------------------|----------|--------------------------------------------------------------------------------|
51-
| `--fuzz-proj-path` (`--target-path`, `--target-proj-path`, deprecated aliases) | Yes | Path to the OSS-Fuzz target project directory (`Dockerfile`, `build.sh`; `project.yaml` optional). |
52-
| `--target-source-path` | No | Optional local source override path. If set, source is synchronized with `rsync -a --delete` into the effective Dockerfile `WORKDIR`. |
53-
| `--target-harness` | Yes (run)| Fuzz target harness binary name. |
51+
### Source-Only Run
5452

55-
Existing [OSS-Fuzz projects](https://github.com/google/oss-fuzz/tree/master/projects) can be used directly as `--fuzz-proj-path` without modification.
53+
```bash
54+
uv run oss-crs run \
55+
--compose-file ./crs-compose.yaml \
56+
--target-source-path /path/to/source
57+
```
5658

57-
### Source Path Semantics
59+
Source-only runs omit `--fuzz-proj-path` entirely. The source path is
60+
directly bind-mounted to `OSS_CRS_TARGET_SOURCE`. There is no build step,
61+
no `OSS_CRS_FUZZ_PROJ` mount, and `SANITIZER`, `ARCHITECTURE`, and
62+
`FUZZING_LANGUAGE` are not injected into source-only containers.
5863

59-
- `OSS_CRS_PROJ_PATH` points to the copied target project directory.
60-
- `OSS_CRS_REPO_PATH` points to the effective final Dockerfile `WORKDIR` inside
61-
the target image.
62-
- `WORKDIR` resolution follows Dockerfile semantics, with fallback chain:
63-
final `WORKDIR` -> `$SRC` -> `/src` (when `SRC` is not provided).
64-
- `libCRS download-source repo` prefers the live runtime source workspace
65-
rooted at `$SRC`/`/src`. When `OSS_CRS_REPO_PATH` is inside that workspace,
66-
the downloaded tree preserves the workspace layout rather than flattening a
67-
nested `WORKDIR`.
68-
- When `--target-source-path` is set, the override source is synchronized into
69-
`OSS_CRS_REPO_PATH` via `rsync -a --delete`.
64+
## Arguments
65+
66+
| Argument | Required | Description |
67+
|----------|----------|-------------|
68+
| `--fuzz-proj-path` (`--target-path`, `--target-proj-path`, deprecated aliases) | Yes for harnessed/harness-gen runs; omitted for source-only runs | Path to the OSS-Fuzz target project directory (`Dockerfile`, `build.sh`; `project.yaml` optional). |
69+
| `--target-source-path` | Required for source-only runs; optional local source override otherwise | Path to the source tree. For source-only runs, this is directly bind-mounted. For harnessed runs, source is synchronized with `rsync -a --delete` into the effective Dockerfile `WORKDIR`. |
70+
| `--target-harness` | Yes (run) | Fuzz target harness binary name. |
71+
72+
At least one of `--fuzz-proj-path` or `--target-source-path` is required.
73+
If `--fuzz-proj-path` is specified, `--target-harness` is required.
7074

71-
### `--target-source-path` Sync Flow
75+
Existing [OSS-Fuzz projects](https://github.com/google.com/oss-fuzz/tree/master/projects) can be used directly as `--fuzz-proj-path` without modification.
76+
77+
## Source Path Semantics
78+
79+
### Harnessed Runs with Source Override
7280

7381
`--target-source-path` is not bind-mounted directly to `OSS_CRS_REPO_PATH`.
7482
Instead, during image build:
@@ -78,3 +86,20 @@ Instead, during image build:
7886
3. `rsync -a --delete /OSS_CRS_REPO_OVERRIDE/ ./` runs from the effective
7987
`WORKDIR`.
8088
4. `OSS_CRS_REPO_PATH` points to that effective `WORKDIR` path.
89+
90+
### Source-Only Runs
91+
92+
For source-only runs, `--target-source-path` is directly bind-mounted to
93+
`OSS_CRS_TARGET_SOURCE`. There is no image build, no `OSS_CRS_FUZZ_PROJ`
94+
mount, and no `SANITIZER`, `ARCHITECTURE`, or `FUZZING_LANGUAGE` injection.
95+
96+
### Common Semantics
97+
98+
- `OSS_CRS_REPO_PATH` points to the effective final Dockerfile `WORKDIR`
99+
inside the target image.
100+
- `WORKDIR` resolution follows Dockerfile semantics, with fallback chain:
101+
final `WORKDIR` -> `$SRC` -> `/src` (when `SRC` is not provided).
102+
- `libCRS download-source repo` prefers the live runtime source workspace
103+
rooted at `$SRC`/`/src`. When `OSS_CRS_REPO_PATH` is inside that workspace,
104+
the downloaded tree preserves the workspace layout rather than flattening a
105+
nested `WORKDIR`.

‎docs/crs-development-guide.md‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ The `crs.yaml` file is the central configuration for your CRS. It tells OSS-CRS
6666
```yaml
6767
name: my-crs
6868
type:
69-
- bug-finding # bug-finding, bug-fixing, or both
69+
- bug-finding # bug-finding, bug-fixing, etc.
7070
version: "1.0.0"
7171
docker_registry: "ghcr.io/my-org/my-crs"
7272

@@ -131,7 +131,7 @@ required_envs:
131131
| Field | Description |
132132
|---|---|
133133
| `name` | Unique name for your CRS |
134-
| `type` | Set of CRS types: `bug-finding`, `bug-fixing` |
134+
| `type` | Set of CRS capabilities: `bug-finding`, `bug-fixing`, etc. |
135135
| `version` | Version string (used as a Docker image tag) |
136136
| `docker_registry` | Docker registry URL for your CRS images |
137137
| `prepare_phase.hcl` | Path to the HCL file for `docker buildx bake` |
@@ -346,7 +346,7 @@ Your containers receive these environment variables automatically:
346346
| `OSS_CRS_NAME` | CRS name (from `crs-compose.yaml`) | `my-crs` |
347347
| `OSS_CRS_SERVICE_NAME` | Full service name | `my-crs_fuzzer` |
348348
| `OSS_CRS_TARGET` | Target project name | `libxml2` |
349-
| `OSS_CRS_TARGET_HARNESS` | Target harness binary name | `xml` |
349+
| `OSS_CRS_TARGET_HARNESS` | Target harness binary name. Unset for no-harness source-level runs. | `xml` |
350350
| `OSS_CRS_CPUSET` | Allocated CPU cores | `4-7` |
351351
| `OSS_CRS_MEMORY_LIMIT` | Memory limit | `16G` |
352352
| `OSS_CRS_BUILD_OUT_DIR` | Build output directory (read-only at run time) | `/OSS_CRS_BUILD_OUT_DIR` |
@@ -799,6 +799,12 @@ Your CRS should submit findings through libCRS:
799799
- **`register-submit-dir`** — Best for high-volume output. Forks a daemon that watches the directory, deduplicates files by hash, and submits in batches. Use this for seeds and PoVs.
800800
- **`submit`** — Best for one-off submissions. Submits a single file immediately.
801801

802+
### Source-Level Bug Finding Without A Harness
803+
804+
Source-only runs are invoked without `--fuzz-proj-path` and instead use `--target-source-path` to point at the source tree. They skip OSS-Fuzz target image builds and require every run module to set `target_dependent: false`; run `oss-crs prepare` first to build those target-independent images. They do not mount `OSS_CRS_BUILD_OUT_DIR` or `OSS_CRS_FUZZ_PROJ`, but still receive `OSS_CRS_SUBMIT_DIR`, `OSS_CRS_FETCH_DIR`, `OSS_CRS_SHARED_DIR`, `OSS_CRS_LOG_DIR`, and `OSS_CRS_TARGET_SOURCE`. They do not receive `OSS_CRS_TARGET_HARNESS`, `SANITIZER`, `ARCHITECTURE`, or `FUZZING_LANGUAGE`.
805+
806+
Source-only runs require all CRSs to be of type `auditing`. This ensures that only CRSs designed to analyze source code without a compiled target can run in source-only mode. An `auditing` CRS is a regular producer that reads `OSS_CRS_TARGET_SOURCE` and submits `bug-candidate` artifacts. The type is a capability label rather than a source-only restriction: auditors may run alone without `--target-harness` or alongside harness-based CRSs.
807+
802808
---
803809

804810
## Fetching Data
@@ -914,6 +920,7 @@ Before publishing your CRS, verify:
914920
- [ ] Run-phase Dockerfiles install libCRS (`COPY --from=libcrs . /opt/libCRS && RUN /opt/libCRS/install.sh`)
915921
- [ ] Containers download build outputs at startup via `libCRS download-build-output`
916922
- [ ] Artifact directories are registered with `libCRS register-submit-dir`
923+
- [ ] Auditing CRSs submit findings as `bug-candidate` artifacts
917924
- [ ] `supported_target` accurately reflects your CRS capabilities
918925
- [ ] `required_llms` lists all models used (if any)
919926
- [ ] `required_inputs` lists inputs the CRS depends on (if any)

‎docs/registry.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ source:
1818
| Field | Description |
1919
|---|---|
2020
| `name` | Unique identifier for the CRS |
21-
| `type` | List of CRS capabilities — `bug-finding`, `bug-fixing`, `bug-finding-triage`, `seed-filter`, `bug-fixing-ensemble`, or a combination |
21+
| `type` | List of CRS capabilities — `bug-finding`, `bug-fixing`, `auditing`, `bug-finding-triage`, `seed-filter`, `bug-fixing-ensemble`, or a combination |
2222
| `source.url` | Git repository URL containing the CRS implementation |
2323
| `source.ref` | Git branch or tag to use |
2424

‎oss_crs/src/cli/archive.py‎

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@
1010
from ..target import Target
1111

1212

13-
def handle_archive(args, crs_compose, target: Target) -> bool:
13+
def handle_archive(
14+
args, crs_compose, target: Target, *, unharnessed: bool = False
15+
) -> bool:
1416
"""Handle the archive command."""
15-
ctx = resolve_run_context(args, crs_compose, target)
17+
ctx = resolve_run_context(args, crs_compose, target, unharnessed=unharnessed)
1618
if ctx is None:
1719
return False
18-
sanitizer, run_id = ctx
19-
harness = target.target_harness
20+
sanitizer, run_id, scope_known = ctx
21+
if not scope_known:
22+
print("No run artifact scope found for the selected run.", file=sys.stderr)
23+
return False
2024
work_dir = crs_compose.work_dir
2125

2226
out_path = Path(args.out)
@@ -25,8 +29,9 @@ def handle_archive(args, crs_compose, target: Target) -> bool:
2529
triage_crs = [crs for crs in crs_compose.crs_list if crs.config.is_triage]
2630
non_triage_crs = [crs for crs in crs_compose.crs_list if not crs.config.is_triage]
2731

28-
# Collect (arcname, src_path) pairs for each artifact subdir
29-
artifact_subdirs = list(SUBMITTED_ARTIFACT_DIR_NAMES)
32+
# Collect (arcname, src_path) pairs for each artifact subdir.
33+
# Include "harnesses" locally (harness-gen output is not exchanged).
34+
artifact_subdirs = list(SUBMITTED_ARTIFACT_DIR_NAMES) + ["harnesses"]
3035
# In a triage run, POVs come from the triage CRS instead (see below).
3136
non_pov_subdirs = [s for s in artifact_subdirs if s not in ("povs", "reports")]
3237

@@ -81,27 +86,26 @@ def _add_dir(collected: list, src_dir: Path, arcname_prefix: str) -> None:
8186

8287
if args.include_all:
8388
# Also include exchange dir, logs, and shared dirs
84-
if harness:
85-
exchange_dir = work_dir.get_exchange_dir(
86-
target, run_id, sanitizer, create=False
89+
exchange_dir = work_dir.get_exchange_dir(
90+
target, run_id, sanitizer, create=False
91+
)
92+
_add_dir(collected, exchange_dir, "exchange")
93+
94+
run_logs_dir = work_dir.get_run_logs_dir(
95+
target, run_id, sanitizer, create=False
96+
)
97+
_add_dir(collected, run_logs_dir, "logs")
98+
99+
for crs in crs_compose.crs_list:
100+
shared_dir = work_dir.get_shared_dir(
101+
crs.name, target, run_id, sanitizer, create=False
87102
)
88-
_add_dir(collected, exchange_dir, "exchange")
103+
_add_dir(collected, shared_dir, f"shared/{crs.name}")
89104

90-
run_logs_dir = work_dir.get_run_logs_dir(
91-
target, run_id, sanitizer, create=False
105+
log_dir = work_dir.get_log_dir(
106+
crs.name, target, run_id, sanitizer, create=False
92107
)
93-
_add_dir(collected, run_logs_dir, "logs")
94-
95-
for crs in crs_compose.crs_list:
96-
shared_dir = work_dir.get_shared_dir(
97-
crs.name, target, run_id, sanitizer, create=False
98-
)
99-
_add_dir(collected, shared_dir, f"shared/{crs.name}")
100-
101-
log_dir = work_dir.get_log_dir(
102-
crs.name, target, run_id, sanitizer, create=False
103-
)
104-
_add_dir(collected, log_dir, f"logs/crs/{crs.name}")
108+
_add_dir(collected, log_dir, f"logs/crs/{crs.name}")
105109

106110
if not collected:
107111
print("No artifacts found for the selected run.", file=sys.stderr)

0 commit comments

Comments
 (0)