Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/hol-plugin-scan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Plugin Security Scan

on:
pull_request:
push:

permissions:
contents: read

jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false

- uses: hashgraph-online/ai-plugin-scanner-action@55616c962cf86368423f7673b2ecdfdbe613d1af # v1.2.515
with:
plugin_dir: "."
min_score: 80
fail_on_severity: high
Original file line number Diff line number Diff line change
Expand Up @@ -315,8 +315,8 @@ git commit -m "feat: add evidence-gated Codex promotion receipts"

```python
def test_wrong_token_is_rejected(tmp_path: Path) -> None:
with running_server(tmp_path, token="expected") as server:
response = send(server, token="wrong", operation="status", payload={})
with running_server(tmp_path, token="<expected-token>") as server:
response = send(server, token="<wrong-token>", operation="status", payload={})
assert response["error_code"] == "AUTH_FAILED"


Expand Down
4 changes: 3 additions & 1 deletion plugins/marginal/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
"longDescription": "MARGINAL measures repeated Codex tool work locally, starts in Shadow Mode, and permits repository Tool Enforcement only after a versioned evidence gate proves coverage, reviewed false stops, and governance overhead.",
"developerName": "SignalLayer Labs",
"category": "Productivity",
"capabilities": [],
"capabilities": [
"tool_enforcement"
],
"websiteURL": "https://signallayerlabs.github.io/Marginal/",
"privacyPolicyURL": "https://signallayerlabs.github.io/Marginal/privacy.html",
"termsOfServiceURL": "https://signallayerlabs.github.io/Marginal/terms.html",
Expand Down
14 changes: 7 additions & 7 deletions scripts/smoke_codex_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def _initialize_repository(path: Path, environment: dict[str, str]) -> None:
_run(["git", "commit", "-qm", "initial"], environment=environment, cwd=path)


def _hook_payloads(workspace: Path, secret: str) -> list[dict[str, Any]]:
def _hook_payloads(workspace: Path, sentinel: str) -> list[dict[str, Any]]:
common: dict[str, Any] = {
"session_id": "smoke-session",
"transcript_path": None,
Expand All @@ -76,7 +76,7 @@ def _hook_payloads(workspace: Path, secret: str) -> list[dict[str, Any]]:
"turn_id": "smoke-turn",
"tool_name": "Bash",
"tool_use_id": "smoke-call",
"tool_input": {"command": f"echo {secret}", "description": secret},
"tool_input": {"command": f"echo {sentinel}", "description": sentinel},
}
return [
{**common, "hook_event_name": "SessionStart", "source": "startup"},
Expand All @@ -86,11 +86,11 @@ def _hook_payloads(workspace: Path, secret: str) -> list[dict[str, Any]]:
]


def _count_secret(root: Path, secret: str) -> int:
def _count_secret(root: Path, sentinel: str) -> int:
occurrences = 0
if not root.exists():
return 0
marker = secret.encode("utf-8")
marker = sentinel.encode("utf-8")
for path in root.rglob("*"):
if path.is_file():
occurrences += path.read_bytes().count(marker)
Expand Down Expand Up @@ -154,14 +154,14 @@ def smoke_plugin(
"PLUGIN_DATA": str(plugin_data),
}

secret = "MARGINAL_SMOKE_SECRET_7fcd98"
sentinel = "MARGINAL_SMOKE_SECRET_7fcd98"
completed_hooks = 0
shadow_blocks = 0
native_control_observed = False
native_control_mode = "unknown"
removed = False
try:
for payload in _hook_payloads(workspace, secret):
for payload in _hook_payloads(workspace, sentinel):
result = _run(
["python3", str(hook_script)],
environment=hook_environment,
Expand Down Expand Up @@ -222,7 +222,7 @@ def smoke_plugin(
native_control_observed=native_control_observed,
native_control_mode=native_control_mode,
launcher_python_version=launcher_python_version,
raw_secret_occurrences=_count_secret(plugin_data, secret),
raw_secret_occurrences=_count_secret(plugin_data, sentinel),
removed=removed,
codex_version=version,
)
Expand Down
6 changes: 3 additions & 3 deletions tests/benchmark/test_codex_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,19 +212,19 @@ def test_run_rejects_runtime_directory_inside_checkout(tmp_path: Path) -> None:


def test_auth_material_is_never_written_to_model_patch(tmp_path: Path) -> None:
secret = "benchmark-secret-value-123456"
sentinel = "benchmark-secret-value-123456"
config = _config(
tmp_path,
condition="baseline",
extra_env={"FAKE_CODEX_LOG": str(tmp_path / "leak-log.json"), "FAKE_LEAK_AUTH": "1"},
auth_content=json.dumps({"tokens": {"access_token": secret}}),
auth_content=json.dumps({"tokens": {"access_token": sentinel}}),
)

record = run_task(config)

assert record["run_status"] == "security_failed"
assert record["error_code"] == "AUTH_MATERIAL_EXFILTRATED"
assert secret not in (config.run_dir / "model.patch").read_text(encoding="utf-8")
assert sentinel not in (config.run_dir / "model.patch").read_text(encoding="utf-8")


@pytest.mark.parametrize(
Expand Down
5 changes: 3 additions & 2 deletions tests/integrations/codex/test_transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,18 @@ def _server(


def test_wrong_token_is_rejected_without_echoing_it(tmp_path: Path) -> None:
bad_credential = "wrong-secret"
with _server(tmp_path) as server:
response = request_session(
server.connection,
operation="status",
payload={},
token="wrong-secret",
token=bad_credential,
)

assert response["ok"] is False
assert response["error_code"] == "AUTH_FAILED"
assert "wrong-secret" not in str(response)
assert bad_credential not in str(response)


def test_authenticated_bounded_request_round_trips(tmp_path: Path) -> None:
Expand Down
Loading