Skip to content

Commit 6450ad2

Browse files
br32710azchin
authored andcommitted
refactor: fix documentation, drop additional env variables in source-only mode
Signed-off-by: Brandon Luo <br32710@ll.mit.edu>
1 parent bec4846 commit 6450ad2

6 files changed

Lines changed: 90 additions & 47 deletions

File tree

docs/config/target-project.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ uv run oss-crs run \
5858

5959
Source-only runs omit `--fuzz-proj-path` entirely. The source path is
6060
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.
61+
no `OSS_CRS_FUZZ_PROJ` mount, and `SANITIZER`, `ARCHITECTURE`,
62+
`FUZZING_LANGUAGE`, etc. not injected into source-only containers.
6363

6464
## Arguments
6565

@@ -70,9 +70,8 @@ no `OSS_CRS_FUZZ_PROJ` mount, and `SANITIZER`, `ARCHITECTURE`, and
7070
| `--target-harness` | Yes (run) | Fuzz target harness binary name. |
7171

7272
At least one of `--fuzz-proj-path` or `--target-source-path` is required.
73-
If `--fuzz-proj-path` is specified, `--target-harness` is required.
7473

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.
74+
Existing [OSS-Fuzz projects](https://github.com/google/oss-fuzz/tree/master/projects) can be used directly as `--fuzz-proj-path` without modification.
7675

7776
## Source Path Semantics
7877

@@ -91,7 +90,8 @@ Instead, during image build:
9190

9291
For source-only runs, `--target-source-path` is directly bind-mounted to
9392
`OSS_CRS_TARGET_SOURCE`. There is no image build, no `OSS_CRS_FUZZ_PROJ`
94-
mount, and no `SANITIZER`, `ARCHITECTURE`, or `FUZZING_LANGUAGE` injection.
93+
mount, and environment variables like `SANITIZER`, `ARCHITECTURE`, `FUZZING_LANGUAGE`,
94+
etc. are not injected.
9595

9696
### Common Semantics
9797

docs/crs-development-guide.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ Your CRS should submit findings through libCRS:
801801

802802
### Source-Level Bug Finding Without A Harness
803803

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`.
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`, `FUZZING_LANGUAGE`, `FUZZING_ENGINE`, `HELPER`, or `RUN_FUZZER_MODE`, since none of these are consumed by source-level analysis.
805805

806806
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.
807807

oss_crs/src/cli/crs_compose.py

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,14 @@ def add_gen_compose_command(subparsers):
626626
)
627627

628628

629+
def _resolve_source_only(args, crs_compose) -> bool:
630+
"""Source-only iff no --target-harness, no --fuzz-proj-path, and the
631+
composition contains no harness-generation CRSs."""
632+
if args.target_harness is not None or args.target_proj_path is not None:
633+
return False
634+
return not any(crs.config.is_harness_gen for crs in crs_compose.crs_list)
635+
636+
629637
def init_target_from_args(
630638
args, *, source_only: bool = False, require_source_dir: bool = False
631639
) -> Target:
@@ -951,12 +959,7 @@ def cli() -> bool | int:
951959
):
952960
return False
953961
elif args.command == "run":
954-
harness_gen = any(crs.config.is_harness_gen for crs in crs_compose.crs_list)
955-
source_only = (
956-
args.target_harness is None
957-
and args.target_proj_path is None
958-
and not harness_gen
959-
)
962+
source_only = _resolve_source_only(args, crs_compose)
960963
try:
961964
target = init_target_from_args(
962965
args, source_only=source_only, require_source_dir=source_only
@@ -1010,14 +1013,7 @@ def cli() -> bool | int:
10101013
if run_rc != 0:
10111014
return run_rc
10121015
elif args.command == "artifacts":
1013-
harness_gen = args.target_harness is None and any(
1014-
crs.config.is_harness_gen for crs in crs_compose.crs_list
1015-
)
1016-
source_only = (
1017-
args.target_harness is None
1018-
and args.target_proj_path is None
1019-
and not harness_gen
1020-
)
1016+
source_only = _resolve_source_only(args, crs_compose)
10211017
try:
10221018
target = init_target_from_args(args, source_only=source_only)
10231019
except ValueError as exc:
@@ -1031,14 +1027,7 @@ def cli() -> bool | int:
10311027
unharnessed=args.target_harness is None,
10321028
)
10331029
elif args.command == "archive":
1034-
harness_gen = args.target_harness is None and any(
1035-
crs.config.is_harness_gen for crs in crs_compose.crs_list
1036-
)
1037-
source_only = (
1038-
args.target_harness is None
1039-
and args.target_proj_path is None
1040-
and not harness_gen
1041-
)
1030+
source_only = _resolve_source_only(args, crs_compose)
10421031
try:
10431032
target = init_target_from_args(args, source_only=source_only)
10441033
except ValueError as exc:

oss_crs/src/env_policy.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -177,18 +177,19 @@ def build_run_service_env(
177177
llm_api_url: str | None = None,
178178
llm_api_key: str | None = None,
179179
) -> EnvPlan:
180-
base_env = {
181-
"HELPER": "True",
182-
"RUN_FUZZER_MODE": "interactive",
183-
**{
184-
k: target_env[v]
185-
for k, v in OSS_FUZZ_TARGET_ENV.items()
186-
if k != "SANITIZER"
187-
and not (source_only and k == "FUZZING_LANGUAGE")
188-
and not (source_only and k == "ARCHITECTURE")
189-
},
190-
}
180+
base_env = {}
191181
if not source_only:
182+
base_env["HELPER"] = "True"
183+
base_env["RUN_FUZZER_MODE"] = "interactive"
184+
# SANITIZER is excluded: its value always comes from the resolved
185+
# sanitizer argument below, never from target_env.
186+
base_env.update(
187+
{
188+
k: target_env[v]
189+
for k, v in OSS_FUZZ_TARGET_ENV.items()
190+
if k != "SANITIZER"
191+
}
192+
)
192193
base_env["SANITIZER"] = sanitizer
193194
base_env["PROJECT_NAME"] = target_env["name"]
194195
# Preserve existing behavior: module env first, CRS env last.

oss_crs/tests/unit/test_env_policy.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,41 @@ def test_source_only_run_env_omits_build_and_fuzz_env() -> None:
172172
assert "OSS_CRS_PROJ_PATH" not in plan.effective_env
173173
assert "SANITIZER" not in plan.effective_env
174174
assert "ARCHITECTURE" not in plan.effective_env
175+
assert "FUZZING_ENGINE" not in plan.effective_env
176+
assert "HELPER" not in plan.effective_env
177+
assert "RUN_FUZZER_MODE" not in plan.effective_env
175178
assert plan.effective_env["OSS_CRS_REPO_PATH"] == "/OSS_CRS_TARGET_SOURCE"
176179

177180

181+
def test_harnessed_run_env_includes_oss_fuzz_runtime_vars() -> None:
182+
"""Harnessed runs keep HELPER, RUN_FUZZER_MODE, and all target env vars."""
183+
plan = build_run_service_env(
184+
target_env={
185+
"engine": "libfuzzer",
186+
"architecture": "x86_64",
187+
"name": "proj",
188+
"language": "c",
189+
"repo_path": "/repo",
190+
},
191+
sanitizer="address",
192+
run_env_type="local",
193+
crs_name="crs-a",
194+
module_name="patcher",
195+
run_id="r1",
196+
cpuset="0-1",
197+
memory_limit="2G",
198+
module_additional_env=None,
199+
crs_additional_env=None,
200+
scope="test:harnessed-run",
201+
)
202+
assert plan.effective_env["HELPER"] == "True"
203+
assert plan.effective_env["RUN_FUZZER_MODE"] == "interactive"
204+
assert plan.effective_env["FUZZING_ENGINE"] == "libfuzzer"
205+
assert plan.effective_env["SANITIZER"] == "address"
206+
assert plan.effective_env["ARCHITECTURE"] == "x86_64"
207+
assert plan.effective_env["FUZZING_LANGUAGE"] == "c"
208+
209+
178210
def test_user_cannot_override_reserved_source_env_vars() -> None:
179211
"""User-provided OSS_CRS_FUZZ_PROJ and OSS_CRS_TARGET_SOURCE are superseded by system values."""
180212
plan = build_target_builder_env(

oss_crs/tests/unit/test_source_only_run.py

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import yaml
55

6-
from oss_crs.src.cli.crs_compose import init_target_from_args
6+
from oss_crs.src.cli.crs_compose import _resolve_source_only, init_target_from_args
77
from oss_crs.src.crs_compose import CRSCompose
88
from oss_crs.src.config.crs import CRSType
99
from oss_crs.src.templates.renderer import render_run_crs_compose_docker_compose
@@ -254,11 +254,32 @@ def test_source_only_rejects_non_auditing_crs() -> None:
254254
assert "auditing" in result.error
255255

256256

257-
def test_harness_gen_without_harness_is_not_source_only() -> None:
258-
target = SimpleNamespace(target_harness=None, source_only=False)
259-
compose = CRSCompose.__new__(CRSCompose)
260-
compose.crs_list = [
261-
SimpleNamespace(config=SimpleNamespace(is_harness_gen=True)),
262-
]
257+
def _source_mode_args(**overrides) -> SimpleNamespace:
258+
defaults: dict = {"target_harness": None, "target_proj_path": None}
259+
defaults.update(overrides)
260+
return SimpleNamespace(**defaults)
261+
262+
263+
def _compose_with(crs_types: list[str]) -> SimpleNamespace:
264+
return SimpleNamespace(
265+
crs_list=[
266+
SimpleNamespace(config=SimpleNamespace(is_harness_gen=t == "harness-gen"))
267+
for t in crs_types
268+
]
269+
)
263270

264-
assert compose.is_source_only_run(target) is False
271+
272+
def test_resolve_source_only_classification_matrix() -> None:
273+
auditing = _compose_with(["auditing"])
274+
harness_gen = _compose_with(["harness-gen"])
275+
276+
assert _resolve_source_only(_source_mode_args(), auditing) is True
277+
assert _resolve_source_only(_source_mode_args(), harness_gen) is False
278+
assert (
279+
_resolve_source_only(_source_mode_args(target_harness="fuzz_target"), auditing)
280+
is False
281+
)
282+
assert (
283+
_resolve_source_only(_source_mode_args(target_proj_path="proj"), auditing)
284+
is False
285+
)

0 commit comments

Comments
 (0)