Extract trust check script inventory helpers

This commit is contained in:
yaojingang
2026-06-16 22:02:30 +08:00
parent a37b760e72
commit ee62291551
12 changed files with 220 additions and 179 deletions
+11 -11
View File
@@ -4,31 +4,24 @@
"generated_at": "2026-06-16",
"skill_dir": ".",
"summary": {
"python_file_count": 204,
"script_file_count": 131,
"python_file_count": 205,
"script_file_count": 132,
"test_file_count": 73,
"internal_module_count": 48,
"cli_script_count": 85,
"cli_script_count": 86,
"command_handler_count": 68,
"entrypoint_command_handler_count": 18,
"command_module_count": 6,
"warn_line_threshold": 900,
"watch_line_threshold": 720,
"block_line_threshold": 1500,
"largest_file_lines": 714,
"largest_file_lines": 707,
"watchlist_count": 0,
"hotspot_count": 0,
"blocker_count": 0,
"decision": "pass"
},
"largest_files": [
{
"path": "scripts/trust_check.py",
"lines": 714,
"kind": "internal-module",
"severity": "pass",
"recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates."
},
{
"path": "scripts/review_studio_gates.py",
"lines": 707,
@@ -105,6 +98,13 @@
"kind": "cli-script",
"severity": "pass",
"recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates."
},
{
"path": "scripts/build_skill_atlas.py",
"lines": 637,
"kind": "cli-script",
"severity": "pass",
"recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates."
}
],
"watchlist": [],
+5 -5
View File
@@ -5,15 +5,15 @@ Generated at: `2026-06-16`
## Summary
- decision: `pass`
- python files: `204`
- scripts: `131`
- python files: `205`
- scripts: `132`
- tests: `73`
- internal modules: `48`
- CLI scripts: `85`
- CLI scripts: `86`
- Yao CLI command handlers: `68`
- entrypoint command handlers: `18`
- command modules: `6`
- largest file lines: `714`
- largest file lines: `707`
- watch threshold lines: `720`
- watchlist: `0`
- hotspots: `0`
@@ -33,7 +33,6 @@ No near-threshold files found.
| File | Lines | Kind | Severity |
| --- | ---: | --- | --- |
| `scripts/trust_check.py` | `714` | `internal-module` | `pass` |
| `scripts/review_studio_gates.py` | `707` | `internal-module` | `pass` |
| `scripts/apply_adaptation.py` | `706` | `cli-script` | `pass` |
| `tests/verify_yao_cli.py` | `696` | `test` | `pass` |
@@ -45,6 +44,7 @@ No near-threshold files found.
| `scripts/render_review_studio.py` | `647` | `cli-script` | `pass` |
| `scripts/render_reference_synthesis.py` | `644` | `cli-script` | `pass` |
| `scripts/cross_packager.py` | `638` | `cli-script` | `pass` |
| `scripts/build_skill_atlas.py` | `637` | `cli-script` | `pass` |
## Release Rule
+10 -10
View File
@@ -6,16 +6,16 @@
"context_budget_tier": "production",
"context_budget_limit": 1000,
"skill_body_tokens": 797,
"other_text_tokens": 1051797,
"other_text_tokens": 1052134,
"estimated_initial_load_tokens": 990,
"estimated_total_text_tokens": 1052594,
"deferred_resource_tokens": 487270,
"estimated_total_text_tokens": 1052931,
"deferred_resource_tokens": 487365,
"deferred_resource_warn_threshold": 120000,
"deferred_resource_dirs": [
{
"path": "scripts",
"estimated_tokens": 427172,
"file_count": 131
"estimated_tokens": 427267,
"file_count": 132
},
{
"path": "references",
@@ -36,8 +36,8 @@
"large_deferred_resource_dirs": [
{
"path": "scripts",
"estimated_tokens": 427172,
"file_count": 131
"estimated_tokens": 427267,
"file_count": 132
}
],
"deferred_resource_governance": {
@@ -59,14 +59,14 @@
],
"missing": [],
"path": "scripts",
"estimated_tokens": 427172,
"file_count": 131,
"estimated_tokens": 427267,
"file_count": 132,
"rationale": "Script resources are deterministic deferred tools, not initial-load prompt context."
}
],
"summary": "Large deferred resources are indexed and backed by evidence."
},
"relevant_file_count": 654,
"relevant_file_count": 656,
"unused_resource_dirs": [],
"quality_signal_points": 130,
"quality_density": 131.3
+1 -1
View File
@@ -2,7 +2,7 @@
| Target | Path | Tier | Limit | Initial | SKILL | Deferred | Resource Governance | Large Deferred Dirs | Quality Density | Unused Dirs | Status |
| --- | --- | --- | ---: | ---: | ---: | ---: | --- | --- | ---: | --- | --- |
| root | `.` | `production` | 1000 | 990 | 797 | 487270 | `governed` | scripts:427172 | 131.3 | - | ok |
| root | `.` | `production` | 1000 | 990 | 797 | 487365 | `governed` | scripts:427267 | 131.3 | - | ok |
| complex-release-orchestrator | `examples/complex-release-orchestrator/generated-skill` | `production` | 1000 | 790 | 718 | 1657 | `not-required` | - | 164.6 | - | ok |
| governed-incident-command | `examples/governed-incident-command/generated-skill` | `production` | 1000 | 760 | 658 | 1030 | `not-required` | - | 171.1 | - | ok |
+5 -5
View File
@@ -8,12 +8,12 @@
"budget_limit": 1000,
"initial_tokens": 990,
"skill_body_tokens": 797,
"deferred_resource_tokens": 487270,
"deferred_resource_tokens": 487365,
"large_deferred_resource_dirs": [
{
"path": "scripts",
"estimated_tokens": 427172,
"file_count": 131
"estimated_tokens": 427267,
"file_count": 132
}
],
"deferred_resource_governance": {
@@ -35,8 +35,8 @@
],
"missing": [],
"path": "scripts",
"estimated_tokens": 427172,
"file_count": 131,
"estimated_tokens": 427267,
"file_count": 132,
"rationale": "Script resources are deterministic deferred tools, not initial-load prompt context."
}
],
+7 -1
View File
@@ -5,7 +5,7 @@
"root": ".",
"summary": {
"target_python": "3.11",
"file_count": 207,
"file_count": 208,
"issue_count": 0,
"syntax_error_count": 0,
"fstring_311_violation_count": 0,
@@ -706,6 +706,12 @@
"issue_count": 0,
"issues": []
},
{
"path": "scripts/trust_check_scripts.py",
"ok": true,
"issue_count": 0,
"issues": []
},
{
"path": "scripts/upgrade_check.py",
"ok": true,
+1 -1
View File
@@ -6,7 +6,7 @@ Generated at: `2026-06-16`
- decision: `pass`
- target python: `3.11`
- files scanned: `207`
- files scanned: `208`
- issues: `0`
- syntax errors: `0`
- f-string 3.11 violations: `0`
+24 -6
View File
@@ -2,9 +2,9 @@
"ok": true,
"skill_dir": ".",
"summary": {
"scanned_files": 222,
"script_count": 131,
"internal_module_count": 45,
"scanned_files": 223,
"script_count": 132,
"internal_module_count": 46,
"secret_findings": 0,
"dependency_files": [
"requirements-ci.txt"
@@ -22,8 +22,8 @@
"help_smoke_failed_count": 0,
"interactive_script_count": 0,
"package_hash_scope": "source-contract-without-generated-reports",
"package_hash_file_count": 222,
"package_sha256": "a163b6e288c5342e683875b47fdfe62989d3f09241c0c8fd86ae615c6d5da7ad"
"package_hash_file_count": 223,
"package_sha256": "9b29a4cc28ab3fb1df2434b22e26b9e92ee04c096f35da350705c4c92307260e"
},
"failures": [],
"warnings": [],
@@ -1600,6 +1600,20 @@
"network_urls": [],
"network_hosts": []
},
{
"path": "scripts/trust_check_scripts.py",
"interface": "internal-module",
"interface_declared": true,
"interface_reason": "Static script inventory helpers imported by trust_check.py.",
"has_argparse": true,
"has_main_guard": true,
"uses_input": false,
"uses_network": false,
"uses_file_write": false,
"uses_subprocess": false,
"network_urls": [],
"network_hosts": []
},
{
"path": "scripts/upgrade_check.py",
"interface": "cli",
@@ -1915,7 +1929,7 @@
"checked_count": 86,
"passed_count": 86,
"failed_count": 0,
"skipped_count": 45,
"skipped_count": 46,
"failed_scripts": [],
"results": [
{
@@ -2896,6 +2910,10 @@
"path": "scripts/skillops_opportunity.py",
"reason": "internal module"
},
{
"path": "scripts/trust_check_scripts.py",
"reason": "internal module"
},
{
"path": "scripts/world_class_evidence_contract.py",
"reason": "internal module"
+6 -5
View File
@@ -1,9 +1,9 @@
# Security Trust Report
- OK: `True`
- Scanned files: `222`
- Scripts: `131`
- Internal script modules: `45`
- Scanned files: `223`
- Scripts: `132`
- Internal script modules: `46`
- Secret findings: `0`
- Network-capable scripts: `3`
- Network policy covered scripts: `3`
@@ -15,8 +15,8 @@
- CLI help smoke failures: `0`
- Interactive scripts: `0`
- Package hash scope: `source-contract-without-generated-reports`
- Package hash files: `222`
- Package SHA256: `a163b6e288c5342e683875b47fdfe62989d3f09241c0c8fd86ae615c6d5da7ad`
- Package hash files: `223`
- Package SHA256: `9b29a4cc28ab3fb1df2434b22e26b9e92ee04c096f35da350705c4c92307260e`
## Failures
@@ -173,6 +173,7 @@
| scripts/telemetry_native_host.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. |
| scripts/trigger_eval.py | cli | False | True | True | False | False | False | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. |
| scripts/trust_check.py | cli | False | True | True | False | False | True | True | Default CLI classification; add SCRIPT_INTERFACE for internal modules. |
| scripts/trust_check_scripts.py | internal-module | True | True | True | False | False | False | False | Static script inventory helpers imported by trust_check.py. |
| scripts/upgrade_check.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. |
| scripts/validate_skill.py | cli | False | True | True | False | False | False | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. |
| scripts/verify_package.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. |
+2 -134
View File
@@ -1,6 +1,5 @@
#!/usr/bin/env python3
import argparse
import ast
import hashlib
import json
import subprocess
@@ -9,20 +8,20 @@ import re
from datetime import date
from pathlib import Path
from typing import Any
from urllib.parse import urlparse
try:
import yaml
except ImportError: # pragma: no cover
yaml = None
from trust_check_scripts import INTERNAL_SCRIPT_INTERFACE, script_inventory
ROOT = Path(__file__).resolve().parent.parent
SCAN_DIRS = ["agents", "assets", "docs", "evals", "references", "runtime", "scripts", "security", "skill-ir", "templates"]
ROOT_FILES = ["SKILL.md", "README.md", "manifest.json", "requirements-ci.txt", "Makefile"]
TEXT_SUFFIXES = {".css", ".js", ".md", ".json", ".jsonl", ".yaml", ".yml", ".py", ".sh", ".txt", ".toml"}
PACKAGE_HASH_SCOPE = "source-contract-without-generated-reports"
INTERNAL_SCRIPT_INTERFACE = "internal-module"
NETWORK_POLICY_REL_PATH = "security/network_policy.json"
PERMISSION_POLICY_REL_PATH = "security/permission_policy.json"
HELP_SMOKE_TIMEOUT_SECONDS = 5.0
@@ -94,137 +93,6 @@ def scan_secrets(skill_dir: Path, files: list[Path]) -> list[dict[str, Any]]:
return findings
def script_inventory(skill_dir: Path) -> list[dict[str, Any]]:
scripts_dir = skill_dir / "scripts"
if not scripts_dir.exists():
return []
inventory = []
for path in sorted(scripts_dir.glob("*.py")):
text = path.read_text(encoding="utf-8", errors="replace")
flags = script_flags(text)
interface = script_interface(text)
urls = extract_url_literals(text)
inventory.append(
{
"path": relpath(skill_dir, path),
"interface": interface["name"],
"interface_declared": interface["declared"],
"interface_reason": interface["reason"],
"has_argparse": "argparse" in text,
"has_main_guard": 'if __name__ == "__main__"' in text,
"uses_input": flags["uses_input"],
"uses_network": flags["uses_network"],
"uses_file_write": flags["uses_file_write"],
"uses_subprocess": flags["uses_subprocess"],
"network_urls": urls,
"network_hosts": sorted({urlparse(url).hostname or "" for url in urls if urlparse(url).hostname}),
}
)
return inventory
def string_assignment(tree: ast.Module, variable_name: str) -> str:
for node in tree.body:
value_node = None
if isinstance(node, ast.Assign):
if any(isinstance(target, ast.Name) and target.id == variable_name for target in node.targets):
value_node = node.value
elif isinstance(node, ast.AnnAssign):
target = node.target
if isinstance(target, ast.Name) and target.id == variable_name:
value_node = node.value
if isinstance(value_node, ast.Constant) and isinstance(value_node.value, str):
return value_node.value
return ""
def script_interface(text: str) -> dict[str, Any]:
try:
tree = ast.parse(text)
except SyntaxError:
match = re.search(r"SCRIPT_INTERFACE\s*=\s*['\"]([^'\"]+)['\"]", text)
reason_match = re.search(r"SCRIPT_INTERFACE_REASON\s*=\s*['\"]([^'\"]+)['\"]", text)
name = match.group(1) if match else "cli"
reason = reason_match.group(1) if reason_match else ""
return {"name": name, "declared": bool(match), "reason": reason}
name = string_assignment(tree, "SCRIPT_INTERFACE")
reason = string_assignment(tree, "SCRIPT_INTERFACE_REASON")
if name:
return {"name": name, "declared": True, "reason": reason}
return {"name": "cli", "declared": False, "reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules."}
def extract_url_literals(text: str) -> list[str]:
values: list[str] = []
try:
tree = ast.parse(text)
for node in ast.walk(tree):
if isinstance(node, ast.Constant) and isinstance(node.value, str):
values.append(node.value)
except SyntaxError:
values = re.findall(r"['\"]([^'\"]+)['\"]", text)
urls = []
seen = set()
for value in values:
for match in re.finditer(r"https?://[^\s'\"<>]+", value):
url = match.group(0).rstrip(").,]")
if url not in seen:
seen.add(url)
urls.append(url)
return urls
def call_name(node: ast.AST) -> str:
if isinstance(node, ast.Name):
return node.id
if isinstance(node, ast.Attribute):
base = call_name(node.value)
return f"{base}.{node.attr}" if base else node.attr
return ""
def script_flags(text: str) -> dict[str, bool]:
try:
tree = ast.parse(text)
except SyntaxError:
return {
"uses_input": bool(re.search(r"\b(?:input|getpass)\s*\(", text)),
"uses_network": bool(re.search(r"\b(?:urlopen|Request)\s*\(|\brequests\.", text)),
"uses_file_write": bool(
re.search(r"\.(?:write_text|write_bytes|mkdir|unlink)\s*\(", text)
or re.search(r"\b(?:open)\s*\([^)]*['\"][wa+x]", text)
or "shutil.rmtree" in text
),
"uses_subprocess": "subprocess." in text,
}
call_nodes = [node for node in ast.walk(tree) if isinstance(node, ast.Call)]
calls = [call_name(node.func) for node in call_nodes]
return {
"uses_input": any(name in {"input", "getpass.getpass"} or name.endswith(".getpass") for name in calls),
"uses_network": any(name in {"urlopen", "Request"} or name.startswith("requests.") for name in calls),
"uses_file_write": any(is_file_write_call(node, call_name(node.func)) for node in call_nodes),
"uses_subprocess": any(name.startswith("subprocess.") for name in calls),
}
def is_file_write_call(node: ast.Call, name: str) -> bool:
if name.endswith((".write_text", ".write_bytes", ".mkdir", ".unlink", ".rmdir")):
return True
if name in {"shutil.copy", "shutil.copy2", "shutil.copytree", "shutil.move", "shutil.rmtree"}:
return True
if name == "zipfile.ZipFile":
return True
if name in {"open", "Path.open"} or name.endswith(".open"):
args = list(node.args)
keywords = {keyword.arg: keyword.value for keyword in node.keywords if keyword.arg}
mode_node = args[1] if len(args) > 1 else keywords.get("mode")
if isinstance(mode_node, ast.Constant) and isinstance(mode_node.value, str):
return any(flag in mode_node.value for flag in ("w", "a", "x", "+"))
return False
def dependency_status(skill_dir: Path) -> dict[str, Any]:
candidates = ["requirements-ci.txt", "requirements.txt", "pyproject.toml", "package-lock.json", "uv.lock", "poetry.lock"]
present = [name for name in candidates if (skill_dir / name).exists()]
+146
View File
@@ -0,0 +1,146 @@
#!/usr/bin/env python3
import ast
import re
from pathlib import Path
from typing import Any
from urllib.parse import urlparse
SCRIPT_INTERFACE = "internal-module"
SCRIPT_INTERFACE_REASON = "Static script inventory helpers imported by trust_check.py."
INTERNAL_SCRIPT_INTERFACE = "internal-module"
def relpath(skill_dir: Path, path: Path) -> str:
return str(path.relative_to(skill_dir))
def script_inventory(skill_dir: Path) -> list[dict[str, Any]]:
scripts_dir = skill_dir / "scripts"
if not scripts_dir.exists():
return []
inventory = []
for path in sorted(scripts_dir.glob("*.py")):
text = path.read_text(encoding="utf-8", errors="replace")
flags = script_flags(text)
interface = script_interface(text)
urls = extract_url_literals(text)
inventory.append(
{
"path": relpath(skill_dir, path),
"interface": interface["name"],
"interface_declared": interface["declared"],
"interface_reason": interface["reason"],
"has_argparse": "argparse" in text,
"has_main_guard": 'if __name__ == "__main__"' in text,
"uses_input": flags["uses_input"],
"uses_network": flags["uses_network"],
"uses_file_write": flags["uses_file_write"],
"uses_subprocess": flags["uses_subprocess"],
"network_urls": urls,
"network_hosts": sorted({urlparse(url).hostname or "" for url in urls if urlparse(url).hostname}),
}
)
return inventory
def string_assignment(tree: ast.Module, variable_name: str) -> str:
for node in tree.body:
value_node = None
if isinstance(node, ast.Assign):
if any(isinstance(target, ast.Name) and target.id == variable_name for target in node.targets):
value_node = node.value
elif isinstance(node, ast.AnnAssign):
target = node.target
if isinstance(target, ast.Name) and target.id == variable_name:
value_node = node.value
if isinstance(value_node, ast.Constant) and isinstance(value_node.value, str):
return value_node.value
return ""
def script_interface(text: str) -> dict[str, Any]:
try:
tree = ast.parse(text)
except SyntaxError:
match = re.search(r"SCRIPT_INTERFACE\s*=\s*['\"]([^'\"]+)['\"]", text)
reason_match = re.search(r"SCRIPT_INTERFACE_REASON\s*=\s*['\"]([^'\"]+)['\"]", text)
name = match.group(1) if match else "cli"
reason = reason_match.group(1) if reason_match else ""
return {"name": name, "declared": bool(match), "reason": reason}
name = string_assignment(tree, "SCRIPT_INTERFACE")
reason = string_assignment(tree, "SCRIPT_INTERFACE_REASON")
if name:
return {"name": name, "declared": True, "reason": reason}
return {"name": "cli", "declared": False, "reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules."}
def extract_url_literals(text: str) -> list[str]:
values: list[str] = []
try:
tree = ast.parse(text)
for node in ast.walk(tree):
if isinstance(node, ast.Constant) and isinstance(node.value, str):
values.append(node.value)
except SyntaxError:
values = re.findall(r"['\"]([^'\"]+)['\"]", text)
urls = []
seen = set()
for value in values:
for match in re.finditer(r"https?://[^\s'\"<>]+", value):
url = match.group(0).rstrip(").,]")
if url not in seen:
seen.add(url)
urls.append(url)
return urls
def call_name(node: ast.AST) -> str:
if isinstance(node, ast.Name):
return node.id
if isinstance(node, ast.Attribute):
base = call_name(node.value)
return f"{base}.{node.attr}" if base else node.attr
return ""
def script_flags(text: str) -> dict[str, bool]:
try:
tree = ast.parse(text)
except SyntaxError:
return {
"uses_input": bool(re.search(r"\b(?:input|getpass)\s*\(", text)),
"uses_network": bool(re.search(r"\b(?:urlopen|Request)\s*\(|\brequests\.", text)),
"uses_file_write": bool(
re.search(r"\.(?:write_text|write_bytes|mkdir|unlink)\s*\(", text)
or re.search(r"\b(?:open)\s*\([^)]*['\"][wa+x]", text)
or "shutil.rmtree" in text
),
"uses_subprocess": "subprocess." in text,
}
call_nodes = [node for node in ast.walk(tree) if isinstance(node, ast.Call)]
calls = [call_name(node.func) for node in call_nodes]
return {
"uses_input": any(name in {"input", "getpass.getpass"} or name.endswith(".getpass") for name in calls),
"uses_network": any(name in {"urlopen", "Request"} or name.startswith("requests.") for name in calls),
"uses_file_write": any(is_file_write_call(node, call_name(node.func)) for node in call_nodes),
"uses_subprocess": any(name.startswith("subprocess.") for name in calls),
}
def is_file_write_call(node: ast.Call, name: str) -> bool:
if name.endswith((".write_text", ".write_bytes", ".mkdir", ".unlink", ".rmdir")):
return True
if name in {"shutil.copy", "shutil.copy2", "shutil.copytree", "shutil.move", "shutil.rmtree"}:
return True
if name == "zipfile.ZipFile":
return True
if name in {"open", "Path.open"} or name.endswith(".open"):
args = list(node.args)
keywords = {keyword.arg: keyword.value for keyword in node.keywords if keyword.arg}
mode_node = args[1] if len(args) > 1 else keywords.get("mode")
if isinstance(mode_node, ast.Constant) and isinstance(mode_node.value, str):
return any(flag in mode_node.value for flag in ("w", "a", "x", "+"))
return False
+2
View File
@@ -101,6 +101,7 @@ def main() -> None:
"scripts/review_studio_formatting.py",
"scripts/review_studio_gates.py",
"scripts/review_studio_layout.py",
"scripts/trust_check_scripts.py",
"scripts/evidence_consistency_core.py",
"scripts/evidence_consistency_release.py",
"scripts/evidence_consistency_skill_os2_review.py",
@@ -135,6 +136,7 @@ def main() -> None:
assert "review_studio_formatting.py" not in warning_text, payload["warnings"]
assert "review_studio_gates.py" not in warning_text, payload["warnings"]
assert "review_studio_layout.py" not in warning_text, payload["warnings"]
assert "trust_check_scripts.py" not in warning_text, payload["warnings"]
assert "evidence_consistency_core.py" not in warning_text, payload["warnings"]
assert "evidence_consistency_release.py" not in warning_text, payload["warnings"]
assert "evidence_consistency_skill_os2_review.py" not in warning_text, payload["warnings"]