@namewta/speculo 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +1 -1
  2. package/dist/src/structured.js +1 -1
  3. package/dist/src/structured.js.map +1 -1
  4. package/package.json +1 -1
  5. package/template/workflows/ops/A-archive-and-learn/A-archive-and-learn.md +2 -0
  6. package/template/workflows/ops/H-computer-hygiene/H-computer-hygiene.md +105 -0
  7. package/template/workflows/ops/H-computer-hygiene/examples/README.md +9 -0
  8. package/template/workflows/ops/H-computer-hygiene/examples/linux/2026-09-14.environment.md +45 -0
  9. package/template/workflows/ops/H-computer-hygiene/examples/linux/2026-09-14.json +970 -0
  10. package/template/workflows/ops/H-computer-hygiene/examples/linux/2026-09-14.md +216 -0
  11. package/template/workflows/ops/H-computer-hygiene/examples/macos/2026-09-14.environment.md +45 -0
  12. package/template/workflows/ops/H-computer-hygiene/examples/macos/2026-09-14.json +970 -0
  13. package/template/workflows/ops/H-computer-hygiene/examples/macos/2026-09-14.md +216 -0
  14. package/template/workflows/ops/H-computer-hygiene/examples/windows/2026-09-14.environment.md +45 -0
  15. package/template/workflows/ops/H-computer-hygiene/examples/windows/2026-09-14.json +969 -0
  16. package/template/workflows/ops/H-computer-hygiene/examples/windows/2026-09-14.md +216 -0
  17. package/template/workflows/ops/H-computer-hygiene/references/00-sources.md +58 -0
  18. package/template/workflows/ops/H-computer-hygiene/references/01-safety.md +52 -0
  19. package/template/workflows/ops/H-computer-hygiene/references/10-windows.md +47 -0
  20. package/template/workflows/ops/H-computer-hygiene/references/11-macos.md +46 -0
  21. package/template/workflows/ops/H-computer-hygiene/references/12-linux.md +13 -0
  22. package/template/workflows/ops/H-computer-hygiene/references/20-toolchains.md +83 -0
  23. package/template/workflows/ops/H-computer-hygiene/references/21-path-migration.md +64 -0
  24. package/template/workflows/ops/H-computer-hygiene/references/22-repositories.md +35 -0
  25. package/template/workflows/ops/H-computer-hygiene/references/30-software-removal.md +41 -0
  26. package/template/workflows/ops/H-computer-hygiene/references/40-report-spec.md +54 -0
  27. package/template/workflows/ops/H-computer-hygiene/references/50-speculo-integration.md +26 -0
  28. package/template/workflows/ops/H-computer-hygiene/references/source-register.json +406 -0
  29. package/template/workflows/ops/H-computer-hygiene/rules/catalog.json +191 -0
  30. package/template/workflows/ops/H-computer-hygiene/scripts/hygiene.py +1348 -0
  31. package/template/workflows/ops/H-computer-hygiene/scripts/run.ps1 +7 -0
  32. package/template/workflows/ops/H-computer-hygiene/scripts/run.sh +5 -0
  33. package/template/workflows/ops/H-computer-hygiene/templates/config.example.json +13 -0
  34. package/template/workflows/ops/H-computer-hygiene/templates/maven-settings.fragment.xml +6 -0
  35. package/template/workflows/ops/H-computer-hygiene/templates/native-evidence.example.json +19 -0
  36. package/template/workflows/ops/H-computer-hygiene/tests/cli_smoke.py +65 -0
  37. package/template/workflows/ops/H-computer-hygiene/tests/make_examples.py +114 -0
  38. package/template/workflows/ops/H-computer-hygiene/tests/test_hygiene.py +486 -0
  39. package/template/workflows/ops/README.md +9 -3
  40. package/template/workflows/ops/common/rules/artifact-contract.md +2 -1
  41. package/template/workflows/ops/common/rules/closure-and-learning.md +2 -0
  42. package/template/workflows/ops/common/schemas/change-status.schema.json +2 -2
  43. package/template/workflows/ops/common/tools/validate-ops.mjs +29 -6
  44. package/template/workflows/ops/manifest.json +1 -1
@@ -0,0 +1,7 @@
1
+ # Read-only audit wrapper. Run with a trusted Python 3.10+ interpreter.
2
+ [CmdletBinding()]
3
+ param([Parameter(ValueFromRemainingArguments = $true)][string[]]$AuditArgs)
4
+ $ErrorActionPreference = 'Stop'
5
+ $scriptFile = Join-Path $PSScriptRoot 'hygiene.py'
6
+ & python $scriptFile audit @AuditArgs
7
+ exit $LASTEXITCODE
@@ -0,0 +1,5 @@
1
+ #!/bin/sh
2
+ # Read-only audit wrapper. No sudo, installation, profile sourcing or cleanup.
3
+ set -eu
4
+ SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
5
+ exec python3 "$SCRIPT_DIR/hygiene.py" audit "$@"
@@ -0,0 +1,13 @@
1
+ {
2
+ "schema_version": 1,
3
+ "project_roots": [],
4
+ "exclude_roots": [],
5
+ "target_root": null,
6
+ "max_entries_per_tree": 20000,
7
+ "max_entries_total": 200000,
8
+ "seconds_per_tree": 3.0,
9
+ "seconds_total": 90.0,
10
+ "project_depth": 3,
11
+ "min_age_days": 14,
12
+ "large_file_mib": 512
13
+ }
@@ -0,0 +1,6 @@
1
+ <!-- EXAMPLE ONLY: merge localRepository into the existing settings.xml.
2
+ Do not replace the whole file: servers/profiles/mirrors may be essential.
3
+ Replace DEVROOT with a verified absolute path; back up local-only artifacts. -->
4
+ <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0">
5
+ <localRepository>DEVROOT/data/maven/repository</localRepository>
6
+ </settings>
@@ -0,0 +1,19 @@
1
+ {
2
+ "schema_version": 1,
3
+ "record_kind": "TEMPLATE_NOT_REAL_EVIDENCE",
4
+ "scan_id": "REPLACE_WITH_REAL_SCAN_ID",
5
+ "collected_at": null,
6
+ "tool": "go",
7
+ "trusted_executable_path": "REPLACE_WITH_VERIFIED_ABSOLUTE_PATH",
8
+ "user_authorized_execution": false,
9
+ "argv": ["env", "GOPATH", "GOBIN", "GOCACHE", "GOMODCACHE", "GOROOT", "GOENV"],
10
+ "executed": false,
11
+ "exit_code": null,
12
+ "version": null,
13
+ "allowed_path_values": {},
14
+ "effective_scope": "unknown until actually queried; CLI/project/IDE may differ",
15
+ "full_stdout_recorded": false,
16
+ "full_environment_recorded": false,
17
+ "source_document_verified_at": null,
18
+ "limitations": ["Example only. Starting even a query can invoke shims, plugins, downloads or telemetry."]
19
+ }
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env python3
2
+ """Linux-only end-to-end CLI test in a temporary HOME; no real account scan."""
3
+ import hashlib
4
+ import json
5
+ import os
6
+ import platform
7
+ import subprocess
8
+ import sys
9
+ import tempfile
10
+ import time
11
+ from pathlib import Path
12
+
13
+
14
+ def main():
15
+ if platform.system() != "Linux":
16
+ print(json.dumps({"status": "skipped", "reason": "Linux fixture test; native platform acceptance is separate"}))
17
+ return 0
18
+ script = Path(__file__).resolve().parents[1] / "scripts/hygiene.py"
19
+ stages = []
20
+ with tempfile.TemporaryDirectory(prefix="hygiene-cli-fixture-") as tmp:
21
+ root = Path(tmp).resolve()
22
+ home = root / "home"
23
+ cache = home / ".npm/_cacache"
24
+ payload = cache / "content-v2/aa/data"
25
+ payload.parent.mkdir(parents=True)
26
+ payload.write_bytes(b"CLI-FIXTURE-ONLY\n")
27
+ original = hashlib.sha256(payload.read_bytes()).hexdigest()
28
+ old = time.time() - 40 * 86400
29
+ for path in sorted(cache.rglob("*"), key=lambda x: len(x.parts), reverse=True):
30
+ os.utime(path, (old, old))
31
+ os.utime(cache, (old, old))
32
+ (home / "bin").mkdir()
33
+ env = {"HOME": str(home), "PATH": str(home / "bin"), "LANG": "C.UTF-8", "PYTHONDONTWRITEBYTECODE": "1"}
34
+ def call(stage, args, expected=0, raw=False):
35
+ result = subprocess.run([sys.executable, str(script), *args], env=env, cwd=root,
36
+ capture_output=True, text=True, timeout=30, check=False)
37
+ stages.append({"stage": stage, "exit_code": result.returncode, "expected": expected})
38
+ if result.returncode != expected:
39
+ raise RuntimeError(f"{stage} failed: {result.stderr}")
40
+ return result.stdout.strip() if raw else (json.loads(result.stdout) if result.stdout.strip() else None)
41
+ version = call("version", ["--version"], raw=True)
42
+ files = call("audit", ["audit", "--output", str(root / "reports")])
43
+ assert payload.exists() and not (home / ".speculo-hygiene").exists()
44
+ review = call("review", ["review", "--plan", files["plan"]])
45
+ assert len(review["actions"]) == 1
46
+ approval = root / "approval.json"
47
+ result = call("approve", ["approve", "--plan", files["plan"], "--ids", review["actions"][0]["id"],
48
+ "--output", str(approval), "--ack", "I_REVIEWED_EACH_ID"])
49
+ args = ["apply", "--plan", files["plan"], "--approval", str(approval), "--confirm", result["apply_confirmation"]]
50
+ call("missing-tools-closed-refused", args, expected=2)
51
+ assert payload.exists()
52
+ applied = call("apply", [*args, "--tools-closed"])
53
+ assert not payload.exists() and applied["freed_disk_space"] is False
54
+ preview = call("restore-preview", ["restore", "--receipt", applied["receipt"]])
55
+ call("restore", ["restore", "--receipt", applied["receipt"], "--confirm", preview["confirmation"]])
56
+ assert payload.exists() and hashlib.sha256(payload.read_bytes()).hexdigest() == original
57
+ output = {"status": "passed", "fixture_only": True, "temporary_home_only": True,
58
+ "version": version, "stages": stages, "restored_payload_hash_matches": True,
59
+ "default_audit_no_source_mutation": True, "no_permanent_deletion": True}
60
+ print(json.dumps(output, ensure_ascii=False, indent=2))
61
+ return 0
62
+
63
+
64
+ if __name__ == "__main__":
65
+ raise SystemExit(main())
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env python3
2
+ """Build explicitly synthetic examples without reading the real user's home/apps.
3
+
4
+ Only isolated tempfile content is audited. Native OS inventories are replaced by
5
+ fixture metadata. Never run this as evidence about a real computer.
6
+ """
7
+ import importlib.util
8
+ import json
9
+ import os
10
+ import tempfile
11
+ import time
12
+ from pathlib import Path
13
+
14
+ WORK = Path(__file__).resolve().parents[1]
15
+ spec = importlib.util.spec_from_file_location("hygiene", WORK / "scripts/hygiene.py")
16
+ h = importlib.util.module_from_spec(spec)
17
+ spec.loader.exec_module(h)
18
+
19
+
20
+ class FixtureAuditor(h.Auditor):
21
+ def platform_inventory(self):
22
+ self.coverage.append(dict(area="applications/startup", status="SIMULATED-NOT-NATIVE",
23
+ detail="Synthetic application metadata only; no real registry, Applications, launchd or service reads."))
24
+ example = {"name": "Example Editor", "version": "1.0-fixture", "system_component": False,
25
+ "action": "fixture candidate; not real software and never uninstalled"}
26
+ if self.system == "Windows":
27
+ example["identity"] = "HKCU/Uninstall/FIXTURE-EXAMPLE-ID"
28
+ else:
29
+ example.update(path=str(self.home / "Applications/Example Editor.app"), bundle_id="invalid.example.editor")
30
+ return {"applications": [example], "startup": [], "persistent_environment": [],
31
+ "errors": ["Real OS inventory intentionally disabled for this example."]}
32
+
33
+
34
+ def populate(home, system):
35
+ h.ensure_private_dir(home)
36
+ for folder in ("bin-primary", "bin-old"):
37
+ name = "java.exe" if system == "Windows" else "java"
38
+ binary = home / folder / name
39
+ binary.parent.mkdir(parents=True)
40
+ binary.write_text("FIXTURE: NOT EXECUTABLE CODE")
41
+ binary.chmod(0o755)
42
+ repo = home / ".m2/repository/example/internal/1.0"
43
+ repo.mkdir(parents=True)
44
+ (repo / "internal-1.0.jar").write_bytes(b"LOCAL_ONLY_FIXTURE" * 1024)
45
+ (home / ".m2/settings.xml").write_text(f"<settings><localRepository>{home / '.m2/repository'}</localRepository><servers><server><password>DO_NOT_EXPORT_DEMO_SECRET</password></server></servers></settings>")
46
+ cargo = home / ".cargo"
47
+ cargo.mkdir()
48
+ (cargo / "credentials.toml").write_text('token="DO_NOT_EXPORT_DEMO_SECRET"')
49
+ (cargo / "config.toml").write_text("# protected fixture config")
50
+ for kind, path in h.default_cache_allowlist(home, system).items():
51
+ relative = {"npm-content-cache": "content-v2/aa/data", "pip-cache": "wheels/aa/demo.whl", "go-build-cache": "aa/demo-a"}[kind]
52
+ f = path / relative
53
+ f.parent.mkdir(parents=True)
54
+ f.write_bytes(b"regenerable-fixture-cache\n" * 2048)
55
+ old = time.time() - 45 * 86400
56
+ for child in sorted(path.rglob("*"), key=lambda p: len(p.parts), reverse=True):
57
+ os.utime(child, (old, old))
58
+ os.utime(path, (old, old))
59
+ project = home / "Projects/example-web"
60
+ project.mkdir(parents=True)
61
+ (project / ".git").mkdir()
62
+ (project / "package.json").write_text('{"packageManager":"pnpm@9.0.0"}')
63
+ for name in ("pnpm-lock.yaml", "package-lock.json", ".nvmrc"):
64
+ (project / name).write_text("fixture")
65
+ (project / "node_modules").mkdir()
66
+ (project / "node_modules/local-note.txt").write_text("May be unique data: do not auto-delete")
67
+ (home / ".zshrc").write_text('export JAVA_HOME="$HOME/old-jdk" # DO_NOT_EXPORT_DEMO_SECRET\n')
68
+ (home / ".profile").write_text('export JAVA_HOME="$HOME/new-jdk"\n')
69
+ downloads = home / "Downloads"
70
+ downloads.mkdir()
71
+ (downloads / "old-installer.zip").write_bytes(b"installer fixture")
72
+ unsafe = downloads / "source-clone"
73
+ unsafe.mkdir()
74
+ (unsafe / ".git").write_text("gitdir: ../../Projects/example-web/.git/worktrees/example")
75
+ delimiter = ";" if system == "Windows" else ":"
76
+ return {"PATH": delimiter.join(str(home / n) for n in ("bin-primary", "bin-old", "bin-primary", "missing-bin")),
77
+ "HOME": str(home), "LOCALAPPDATA": str(home / "AppData/Local"),
78
+ "JAVA_HOME": str(home / "old-jdk"), "CARGO_HOME": str(cargo),
79
+ "GOCACHE": str(h.default_cache_allowlist(home, system)["go-build-cache"])}
80
+
81
+
82
+ def main():
83
+ generated = []
84
+ for system, folder in (("Windows", "windows"), ("Darwin", "macos"), ("Linux", "linux")):
85
+ with tempfile.TemporaryDirectory(prefix="hygiene-demo-") as temporary:
86
+ home = Path(temporary).resolve() / "fixture-home"
87
+ env = populate(home, system)
88
+ auditor = FixtureAuditor(home=home, system=system, environ=env,
89
+ options={"project_roots": [str(home / "Projects"), str(home / "Downloads")], "seconds_per_tree": 10},
90
+ label="SYNTHETIC_FIXTURE_NOT_YOUR_COMPUTER", native_platform_reads=False)
91
+ report, plan = auditor.run()
92
+ report["host"].update(release="SIMULATED_OS_METADATA", machine="SIMULATED_ARCHITECTURE",
93
+ elevated=False, fingerprint="FIXTURE_NOT_FOR_APPROVAL",
94
+ disk_total_bytes=512 * 1024**3, disk_free_bytes=120 * 1024**3,
95
+ execution_context_signals={"fixture_only": True})
96
+ report["scope"]["limitations"].append("Disk capacity, architecture and application identity are illustrative fixture values, not real measurements.")
97
+ report["findings"] = [x for x in report["findings"] if x["category"] != "privilege"]
98
+ report["summary"]["findings"] = len(report["findings"])
99
+ output = WORK / "examples" / folder
100
+ output.mkdir(parents=True, exist_ok=True)
101
+ date = report["date"]
102
+ md = h.render_report(report, plan)
103
+ public = h.redact(report, home)
104
+ assert "DO_NOT_EXPORT_DEMO_SECRET" not in md + json.dumps(public)
105
+ (output / f"{date}.md").write_text(md, encoding="utf-8")
106
+ (output / f"{date}.json").write_bytes(h.json_bytes(public))
107
+ target = h.target_root(home, system, env, None)
108
+ (output / f"{date}.environment.md").write_text("**模拟示例,不是已执行迁移。**\n\n" + h.render_environment(report, target), encoding="utf-8")
109
+ generated.append({"system": system, "example": str(output / f"{date}.md"), "findings": len(report["findings"]), "quarantine_candidates": len(plan["actions"])})
110
+ print(json.dumps({"fixture_only": True, "generated": generated}, ensure_ascii=False, indent=2))
111
+
112
+
113
+ if __name__ == "__main__":
114
+ main()