From 51b69bc2915ad854e13bcd92787dc6300b4acd76 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 22 Sep 2026 20:47:12 +0000 Subject: [PATCH] docs: mark RUN COMMAND as trusted-only --- .forgejo/workflows/ci.yml | 2 ++ docs/CURRENT_STATUS.md | 2 ++ docs/HARDENING.md | 10 ++++++++++ docs/PRACTICAL_SCRIPTING.md | 2 ++ tools/validate_trusted_command_docs.py | 24 ++++++++++++++++++++++++ 5 files changed, 40 insertions(+) create mode 100644 tools/validate_trusted_command_docs.py diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index a707503..ea7fbda 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -25,6 +25,8 @@ jobs: run: python3 tools/validate_package_security.py - name: Validate compiler warnings run: python3 tools/validate_compiler_warnings.py + - name: Validate trusted command documentation + run: python3 tools/validate_trusted_command_docs.py - name: Validate CI workflow coverage run: python3 tools/validate_ci_workflow.py - name: Check lessons diff --git a/docs/CURRENT_STATUS.md b/docs/CURRENT_STATUS.md index e5b68d7..0cc3715 100644 --- a/docs/CURRENT_STATUS.md +++ b/docs/CURRENT_STATUS.md @@ -31,6 +31,8 @@ This run's narrow memory slice releases the previous deep value when a runtime v The `RUN COMMAND` path now decodes POSIX `pclose()` wait status before storing `LASTEXIT`, so a child that exits with code 3 exposes `3` rather than the encoded status `768`. Focused coverage is `tests/42_last_exit_code.claro`. HTTP responses now have a 1,048,576-byte cap and marker-like response bodies are preserved while extracting the final HTTP status marker. Focused coverage is `tools/validate_http_hardening.py`. Remaining memory-growth areas include final runtime teardown, loaded program storage, and other expression temporaries. Claro remains a trusted-script interpreter, not a sandbox. +`RUN COMMAND` is documented and validated as a trusted-code capability: it executes shell commands with the user's permissions and is not a sandbox. Claro does not claim untrusted-script safety or use a fragile blacklist sanitizer. Focused documentation coverage is `tools/validate_trusted_command_docs.py`. + ## Feature matrix ### Beginner scripting core diff --git a/docs/HARDENING.md b/docs/HARDENING.md index 1bed77f..b1848ba 100644 --- a/docs/HARDENING.md +++ b/docs/HARDENING.md @@ -42,3 +42,13 @@ python3 tools/validate_http_hardening.py ``` This validator uses a local HTTP server to check marker-like response text, status `200`, and the oversized-response diagnostic. + +## External command trust boundary + +`RUN COMMAND` intentionally executes a shell command with the user's permissions. It is a trusted-code capability, not a sandbox or an untrusted-script safety feature. Claro does not attempt a fragile blacklist sanitizer; users must review scripts before running them. + +Focused documentation verification: + +```text +python3 tools/validate_trusted_command_docs.py +``` diff --git a/docs/PRACTICAL_SCRIPTING.md b/docs/PRACTICAL_SCRIPTING.md index 611120c..09cc3f7 100644 --- a/docs/PRACTICAL_SCRIPTING.md +++ b/docs/PRACTICAL_SCRIPTING.md @@ -64,3 +64,5 @@ SAY LASTEXIT ``` Use this carefully. It runs commands on the user's computer. + +`RUN COMMAND` is for trusted code only. It is not a sandbox: it can start programs, read or change files, and use the same permissions as the user running Claro. Do not run scripts from an untrusted source, and do not treat this feature as a security boundary. diff --git a/tools/validate_trusted_command_docs.py b/tools/validate_trusted_command_docs.py new file mode 100644 index 0000000..0bb6f62 --- /dev/null +++ b/tools/validate_trusted_command_docs.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +"""Ensure the learner-facing RUN COMMAND docs describe its trust boundary.""" +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +DOC = ROOT / "docs" / "PRACTICAL_SCRIPTING.md" + + +def main() -> int: + text = DOC.read_text(encoding="utf-8").lower() + required = ( + "trusted code", + "not a sandbox", + "runs commands on the user's computer", + ) + missing = [phrase for phrase in required if phrase not in text] + if missing: + raise SystemExit("RUN COMMAND documentation is missing: " + ", ".join(missing)) + print("Trusted command documentation validation complete") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())