opencode-lookout 0.0.0-stage → 0.1.2
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.
- package/CHANGELOG.md +75 -0
- package/CODE_OF_CONDUCT.md +20 -0
- package/CONTRIBUTING.md +95 -0
- package/GOVERNANCE.md +20 -0
- package/LICENSE +21 -0
- package/README.md +239 -2
- package/SECURITY.md +35 -0
- package/SUPPORT.md +20 -0
- package/bin/lookout.mjs +99 -0
- package/dist/tui.js +534 -0
- package/docs/ARCHITECTURE.md +124 -0
- package/docs/RELEASE_GATES_PLAN.md +67 -0
- package/docs/RELEASING.md +171 -0
- package/docs/THREAT_MODEL.md +176 -0
- package/docs/VALIDATION.md +99 -0
- package/package.json +103 -4
- package/src/catalog.mjs +278 -0
- package/src/compatibility.mjs +17 -0
- package/src/config.mjs +250 -0
- package/src/controller.mjs +199 -0
- package/src/plugin-description.mjs +90 -0
- package/src/rpc.ts +46 -0
- package/src/server.ts +159 -0
- package/src/tui.tsx +522 -0
- package/src/types.ts +58 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Release assurance implementation plan
|
|
2
|
+
|
|
3
|
+
## Goal and boundaries
|
|
4
|
+
|
|
5
|
+
Add fail-closed release validation without publishing a release, installing a
|
|
6
|
+
self-hosted runner or spending on hosted services. Apply the approved naming break
|
|
7
|
+
below. Preserve the installed checkout; work on `feat/release-gates`.
|
|
8
|
+
|
|
9
|
+
## Authorized naming migration
|
|
10
|
+
|
|
11
|
+
The maintainer approved a clean naming break, superseding the compatibility
|
|
12
|
+
requirement. New identifiers are `opencode-lookout`, `/lookout`,
|
|
13
|
+
`bin/lookout.mjs`, and `lookout:` markers. No legacy aliases or preference
|
|
14
|
+
migration are required. Do not touch the installed copy, personal configuration,
|
|
15
|
+
or private host storage. Reinstallation is deferred until after remote release.
|
|
16
|
+
|
|
17
|
+
Tests must cover damaged markers, idempotent load/unload, retained restrictions,
|
|
18
|
+
rejected self-disables, and offline recovery. Public GitHub already uses
|
|
19
|
+
`opencode-lookout`; verify hosted state before delivery. Old restrictions must be
|
|
20
|
+
restored with the old checkout before reinstalling; never silently remove them.
|
|
21
|
+
|
|
22
|
+
## Execution checkpoints
|
|
23
|
+
|
|
24
|
+
The maintainer chose the current policy API rather than an adapter for OpenCode
|
|
25
|
+
2.2.1/2.2.2, then approved a support-aware test policy after the first realistic
|
|
26
|
+
matrix test exposed the API change. Controls require V2 2.2.3+. All five versions
|
|
27
|
+
remain mandatory: 2.2.1/2.2.2 must prove read-only inventory, rejected mutations,
|
|
28
|
+
offline lifecycle/recovery, and both terminal browsing flows. Other versions
|
|
29
|
+
must pass full controls and both terminal flows. Unknown versions do not become
|
|
30
|
+
read-only skips. Evidence records and validates each target's profile.
|
|
31
|
+
|
|
32
|
+
- [x] Quality, dependency remediation, and naming migration: `src/`, `bin/`,
|
|
33
|
+
existing tests and docs; run `make check` and both isolated terminal cases.
|
|
34
|
+
- [x] Scanner/evidence validation: `scripts/`, failure fixtures in `test/`;
|
|
35
|
+
reject every finding, error, missing report, and source mismatch.
|
|
36
|
+
- [ ] Docker matrix and hosted gates: discover and exercise five stable versions
|
|
37
|
+
and the next RC, then validate workflows and repository protection.
|
|
38
|
+
- [ ] Portable editor/agent guidance and final delivery evidence.
|
|
39
|
+
|
|
40
|
+
## Work units
|
|
41
|
+
|
|
42
|
+
1. **Quality and documentation:** enable checked JavaScript alongside strict
|
|
43
|
+
TypeScript, add lint/JSDoc checks, document module contracts, and run `make check`.
|
|
44
|
+
2. **Release evidence:** add tested parsers that reject any scanner finding/error,
|
|
45
|
+
missing/stale evidence, nonzero test results, and incomplete compatibility sets.
|
|
46
|
+
Audit all dependency classes and full Git history; never use severity waivers.
|
|
47
|
+
3. **Containers:** discover the newest five stable `@opencode/cli` versions plus
|
|
48
|
+
the newest RC for the next release when present. Run actual server/TUI flows in
|
|
49
|
+
isolated local Docker containers, bound to immutable source and CLI artifacts.
|
|
50
|
+
4. **Hosted gates:** run quality, OSS security, dependency/supply-chain, and container
|
|
51
|
+
compatibility checks on public standard Ubuntu runners. Avoid artifact uploads,
|
|
52
|
+
caches, paid scanners, and privileged PR jobs. Manual release assessment also
|
|
53
|
+
requires matching local evidence and human review; it does not publish.
|
|
54
|
+
5. **Governance:** configure single-maintainer protections (zero required external
|
|
55
|
+
approvals), security settings, release documentation, and explicit support limits.
|
|
56
|
+
|
|
57
|
+
## Acceptance and failure paths
|
|
58
|
+
|
|
59
|
+
Test low/unknown-severity findings, scanner errors and empty/malformed reports;
|
|
60
|
+
stale/mismatched source evidence; fewer than five stable versions; a newly
|
|
61
|
+
available RC; failed/missing matrix entries; and container cleanup. A blocked
|
|
62
|
+
upstream dependency is a release blocker, not permission to weaken the policy.
|
|
63
|
+
|
|
64
|
+
All code needs typed public contracts or checked JSDoc, with side effects and
|
|
65
|
+
failure/recovery behavior explained. A clean result means only no findings in the
|
|
66
|
+
configured checks at that time. It is not a guarantee that vulnerabilities do not
|
|
67
|
+
exist. No new platform or CLI support claim is made until its test passes.
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Repository metadata and releases
|
|
2
|
+
|
|
3
|
+
The GitHub repository description should explain what Lookout does without
|
|
4
|
+
claiming official OpenCode affiliation or verified security. Use topic tags for
|
|
5
|
+
discovery: `opencode`, `opencode-plugin`, `tui`, `terminal`, `sidebar`, `typescript`,
|
|
6
|
+
and `developer-tools`. Topics are not Git release tags.
|
|
7
|
+
|
|
8
|
+
## Version and tag policy
|
|
9
|
+
|
|
10
|
+
Use [Semantic Versioning](https://semver.org/) and annotated Git tags named
|
|
11
|
+
`vMAJOR.MINOR.PATCH`. During 0.x development, describe compatibility changes
|
|
12
|
+
explicitly in the changelog; do not treat a minor bump as a migration plan.
|
|
13
|
+
Prereleases use suffixes such as `v0.2.0-rc.1`. Never move or replace a published
|
|
14
|
+
tag. Create a new version if a release needs correction.
|
|
15
|
+
|
|
16
|
+
Tags identify reviewed source revisions. They do not publish an npm package,
|
|
17
|
+
certify provider readiness, or install an update for users. There is
|
|
18
|
+
no automatic publishing workflow. v0.1.2 introduces a separate, manually
|
|
19
|
+
dispatched npm workflow; the older source-release tags remain unchanged.
|
|
20
|
+
|
|
21
|
+
## Release checklist
|
|
22
|
+
|
|
23
|
+
1. Review the exact commit, including security, compatibility, recovery, dependency,
|
|
24
|
+
and license impact. Record the maintainer's approval and commit ID in the PR
|
|
25
|
+
or release notes. Automated review does not satisfy this step.
|
|
26
|
+
2. Update package and lockfile versions together. Move the relevant changelog
|
|
27
|
+
entries from **Unreleased** to a dated version section.
|
|
28
|
+
3. On the clean candidate, run `make check`, `npm run security`,
|
|
29
|
+
`npm run test:compatibility`, and `npm run release:check`. Require all hosted
|
|
30
|
+
CI jobs, including **Release gate**, to pass on that same revision. Record the
|
|
31
|
+
source digest, CLI versions/artifacts, container architecture, and Node versions.
|
|
32
|
+
The required **Analyze** checks must come from full `push` or
|
|
33
|
+
`workflow_dispatch` runs on the candidate, not the separate **PR Analyze**
|
|
34
|
+
checks. After merging, verify the full checks on `main` too and confirm its
|
|
35
|
+
source tree is identical to the approved candidate before publishing.
|
|
36
|
+
4. Inspect tracked files and full history for private data. Every configured
|
|
37
|
+
finding at any severity, scanner error, missing result, stale receipt, or
|
|
38
|
+
incomplete compatibility result blocks. Do not waive or suppress findings to
|
|
39
|
+
make a release pass. Fix the root cause or leave the release blocked.
|
|
40
|
+
5. Create a signed annotated tag if signing is available, or an annotated tag
|
|
41
|
+
otherwise. Confirm the tag targets the approved commit before pushing it.
|
|
42
|
+
Push only that named tag; do not use a blanket tag push.
|
|
43
|
+
6. Create a GitHub release deliberately, with behavior changes, compatibility,
|
|
44
|
+
validation, recovery, and known limitations. Do not attach personal config,
|
|
45
|
+
logs, credentials, or retained test fixtures.
|
|
46
|
+
|
|
47
|
+
Public repository creation is not an initial release. Leave changes under
|
|
48
|
+
**Unreleased** and omit a release tag until this checklist is satisfied.
|
|
49
|
+
|
|
50
|
+
## Scanner prerequisites and evidence
|
|
51
|
+
|
|
52
|
+
Install Semgrep 1.182.0, Trivy 0.76.1, Gitleaks 8.30.1, Docker, and Node 26.4.0+.
|
|
53
|
+
CI installs checksum-verified binaries and uses a digest-pinned
|
|
54
|
+
Semgrep image; see `scripts/install-ci-scanners.sh`. Semgrep rules are downloaded
|
|
55
|
+
from commit `16b9e6d45f10649f6a531ec4838ed9924372c3bc` with a pinned checksum.
|
|
56
|
+
Only JavaScript/TypeScript language security and GitHub Actions security packs
|
|
57
|
+
are selected. React-specific style/i18n rules are not applicable to this Solid TUI;
|
|
58
|
+
registry packs requiring the paid Pro engine are not silently counted as passing.
|
|
59
|
+
The rules retain the upstream Semgrep Rules License and are not vendored as MIT.
|
|
60
|
+
|
|
61
|
+
All receipts live in ignored `release-evidence/`. Release assessment requires
|
|
62
|
+
receipts no older than 24 hours and re-discovers CLI targets; a new RC invalidates
|
|
63
|
+
an older matrix. Missing tools, registry outages, scanner database failures, and
|
|
64
|
+
nonzero test statuses block instead of falling back to previous success. Re-run
|
|
65
|
+
after correcting the cause. Failed runs must never be presented as passes.
|
|
66
|
+
|
|
67
|
+
The matrix tests supported behavior, not identical features on every CLI. The
|
|
68
|
+
maintainer-approved profiles are **read-only** for 2.2.1/2.2.2 and **controls**
|
|
69
|
+
for the remaining targets. Read-only cases must prove rejected mutations, safe
|
|
70
|
+
inventory, lifecycle, and both terminal browsing flows. Control cases must prove
|
|
71
|
+
effective skill/MCP/plugin restrictions and recovery as well. Every case must
|
|
72
|
+
pass its explicit profile; any new unsupported CLI blocks until reviewed.
|
|
73
|
+
|
|
74
|
+
## Single-maintainer hosted protection
|
|
75
|
+
|
|
76
|
+
Require a pull request, resolved conversations, and successful **Runtime (Node 22)**,
|
|
77
|
+
**Runtime (Node 24)**, **Quality (Node 26)**, **Security**, **Compatibility**, and
|
|
78
|
+
**Release gate** checks, plus CodeQL's **Analyze (actions)** and
|
|
79
|
+
**Analyze (javascript-typescript)**. The CI aggregate does not replace those
|
|
80
|
+
separate hosted CodeQL checks. Each CodeQL job validates SARIF for every finding
|
|
81
|
+
and analysis error after the analyze action succeeds.
|
|
82
|
+
Full scans run on every repository branch push. PR scans remain available as
|
|
83
|
+
**PR Analyze (actions)** and **PR Analyze (javascript-typescript)**, but their
|
|
84
|
+
change-focused results cannot substitute for full-candidate assurance. For a fork
|
|
85
|
+
PR, the maintainer must bring the reviewed candidate onto a repository branch
|
|
86
|
+
and obtain the required full checks; do not bypass protection to merge it.
|
|
87
|
+
Use zero required external approvals: the sole maintainer reviews the exact
|
|
88
|
+
revision. Disable force pushes/deletions and apply the checks to administrators.
|
|
89
|
+
Remote protection must be verified live; this document alone does not enable it.
|
|
90
|
+
|
|
91
|
+
Only standard public GitHub runners are used. No self-hosted runners, artifact
|
|
92
|
+
uploads, or cache actions are configured. Runner logs still have normal GitHub
|
|
93
|
+
retention. Do not add paid runners or external service requirements implicitly.
|
|
94
|
+
Never reuse a token from conversation history.
|
|
95
|
+
|
|
96
|
+
## Manual npm publication (v0.1.2 onward)
|
|
97
|
+
|
|
98
|
+
Preparation does not authorize publication. Obtain exact-revision maintainer
|
|
99
|
+
approval after reviewing packaging, generated TUI, consumer audits and installed
|
|
100
|
+
server/TUI evidence. Complete the GitHub release checklist first, including full
|
|
101
|
+
candidate and post-merge CodeQL. The approved tag and main must have identical
|
|
102
|
+
source trees. Re-run stale hosted checks: every required job must have completed
|
|
103
|
+
successfully within 24 hours. A newer failed or incomplete workflow blocks; the
|
|
104
|
+
validator does not fall back to an older success.
|
|
105
|
+
|
|
106
|
+
Configure an npm trusted publisher for GitHub owner `sodejm`, repository
|
|
107
|
+
`opencode-lookout`, workflow filename `npm-publish.yml`, environment `npm`.
|
|
108
|
+
Use GitHub-hosted standard runners. Configure the GitHub `npm` environment to
|
|
109
|
+
allow reviewed release tags and require the sole maintainer's approval; do not
|
|
110
|
+
require a second reviewer. Verify these settings live. Give the publisher only
|
|
111
|
+
the required publish permission, not dist-tag administration. No npm token is
|
|
112
|
+
stored in the repository or workflow. See the current
|
|
113
|
+
[npm trusted publishing guide](https://docs.npmjs.com/trusted-publishers/).
|
|
114
|
+
|
|
115
|
+
**First-package bootstrap remains an account-setup decision.** npm's
|
|
116
|
+
[trust prerequisites](https://docs.npmjs.com/cli/v11/commands/npm-trust) require the
|
|
117
|
+
package to already exist on the registry. A registry
|
|
118
|
+
404 does not establish ownership, and local `npm whoami` is not an OIDC permission
|
|
119
|
+
check. If package settings are not available for this new name, stop and agree a
|
|
120
|
+
bootstrap procedure with the maintainer. Do not publish a placeholder, use the
|
|
121
|
+
revoked token, silently switch to token publishing, or claim OIDC provenance for
|
|
122
|
+
a local publish. Login/2FA must happen in the maintainer's own terminal or npm UI,
|
|
123
|
+
never by sharing credentials in chat.
|
|
124
|
+
|
|
125
|
+
### Approved one-time v0.1.2 bootstrap
|
|
126
|
+
|
|
127
|
+
The maintainer approved publishing the real first `opencode-lookout@0.1.2`
|
|
128
|
+
artifact using the existing authenticated npm login, with 2FA completed privately.
|
|
129
|
+
This is a one-time exception to OIDC publication, not permission to publish a
|
|
130
|
+
placeholder or use the revoked token. It does not waive any quality, security,
|
|
131
|
+
compatibility, signed-tag, GitHub-release, exact-revision approval, or post-merge
|
|
132
|
+
gate above. Record the approved SHA and tested tarball integrity in the release.
|
|
133
|
+
|
|
134
|
+
Before publishing, verify the registry still has no package, verify account
|
|
135
|
+
identity, and run `node scripts/check-package-evidence.mjs PATH_TO_TESTED_TARBALL`
|
|
136
|
+
on the clean approved checkout with fresh evidence. Publish those exact tarball
|
|
137
|
+
bytes with `npm publish PATH_TO_TESTED_TARBALL --access public --ignore-scripts`
|
|
138
|
+
from the maintainer's terminal. Never send credentials or 2FA codes through chat.
|
|
139
|
+
If publication is interrupted, query the registry before retrying; do not attempt
|
|
140
|
+
to replace an existing version.
|
|
141
|
+
|
|
142
|
+
This version has **no GitHub OIDC provenance**. Verify its registry integrity
|
|
143
|
+
against the approved artifact and test a fresh registry installation, including
|
|
144
|
+
offline recovery and both isolated terminal modes. After the package exists,
|
|
145
|
+
configure the trusted publisher described above for future versions. Do not
|
|
146
|
+
dispatch the OIDC workflow for the already-published v0.1.2: npm versions are
|
|
147
|
+
immutable. This exception expires after the first successful v0.1.2 publication.
|
|
148
|
+
|
|
149
|
+
### Subsequent trusted publications
|
|
150
|
+
|
|
151
|
+
After account setup and exact approval, manually dispatch **Publish npm (manual)**
|
|
152
|
+
on the published tag (not `main`) with `approved_sha` set to its full commit ID:
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
gh workflow run npm-publish.yml --ref vX.Y.Z -f approved_sha=FULL_APPROVED_COMMIT
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The workflow verifies the signed annotated tag through GitHub, the non-draft
|
|
159
|
+
GitHub release, the approved SHA and fresh hosted CI/full-CodeQL runs. It installs
|
|
160
|
+
with scripts disabled, reruns security, Docker and release assessment, packs and
|
|
161
|
+
tests the actual tarball, and checks its integrity against Docker evidence. It
|
|
162
|
+
rechecks hosted state immediately before `npm publish --provenance` using OIDC.
|
|
163
|
+
The job has `id-token: write` only for this manual operation; PR CI cannot publish.
|
|
164
|
+
Provenance binds the workflow's tag revision to the artifact, not to human review.
|
|
165
|
+
|
|
166
|
+
After publication, verify the registry's version, tarball integrity and provenance
|
|
167
|
+
against the workflow output and approved tag. Install the registry artifact in a
|
|
168
|
+
fresh isolated prefix and rerun packed recovery/server/TUI validation before
|
|
169
|
+
claiming npm delivery complete. If the job is interrupted after publication,
|
|
170
|
+
inspect registry state first: published versions are immutable, and the workflow
|
|
171
|
+
rejects an already-present version rather than attempting to replace it.
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Threat model
|
|
2
|
+
|
|
3
|
+
## npm publication and consumer boundary
|
|
4
|
+
|
|
5
|
+
The npm artifact uses a files allowlist and has no install hooks. Its committed
|
|
6
|
+
generated TUI is byte-checked against source and included in security coverage.
|
|
7
|
+
An isolated consumer install must pass the same all-severity audit parser, without
|
|
8
|
+
assuming dependency overrides propagate from this repository. They do not:
|
|
9
|
+
installing `@opencode/plugin` as a consumer dependency reproduced vulnerable
|
|
10
|
+
glob/minimatch transitive resolutions. OpenCode supplies the SDK/UI runtime;
|
|
11
|
+
optional peers document that boundary without installing a second copy. The
|
|
12
|
+
offline CLI needs only jsonc-parser. Host-runtime security remains an OpenCode
|
|
13
|
+
responsibility, not a claim made by a clean Lookout consumer audit.
|
|
14
|
+
|
|
15
|
+
npm bin symlinks must execute the CLI rather than silently returning success.
|
|
16
|
+
Tests cover missing, malformed and ambiguous configs, edited ownership markers,
|
|
17
|
+
confirmation refusal and recovery while preserving unrelated denies. Only stable
|
|
18
|
+
package paths are owned; direct registry entries and other config layers can
|
|
19
|
+
override a global disable and require separate inspection.
|
|
20
|
+
|
|
21
|
+
The manual publication job has short-lived OIDC authority in the `npm` environment,
|
|
22
|
+
not a stored npm token. It requires a verified annotated tag at the approved SHA,
|
|
23
|
+
a non-draft stable GitHub release, matching main tree and fresh complete candidate
|
|
24
|
+
and post-merge CI/full CodeQL runs. It reruns local gates and verifies that the
|
|
25
|
+
published tarball hash is the one exercised by Docker. Missing, malformed, stale,
|
|
26
|
+
failed or mismatched evidence blocks publication. Release evidence endpoints
|
|
27
|
+
are queried using validated public GitHub dispatch identifiers; local manifest
|
|
28
|
+
data is compared locally and is not used to construct outbound request URLs.
|
|
29
|
+
Environment/package settings are external controls and must be checked by the
|
|
30
|
+
maintainer before first use.
|
|
31
|
+
Repository write access and compromised dependencies/runners remain trusted risks.
|
|
32
|
+
|
|
33
|
+
The maintainer-approved first-package v0.1.2 bootstrap uses an existing npm login
|
|
34
|
+
and private 2FA instead of OIDC, because npm requires an existing package before
|
|
35
|
+
trusted-publisher setup. The maintainer's terminal and npm session are additional
|
|
36
|
+
trusted components for this one publication. All release gates and exact-artifact
|
|
37
|
+
checks still apply, but this version cannot claim GitHub OIDC provenance. No
|
|
38
|
+
credential enters repository files, CI secrets, or chat. The exception expires
|
|
39
|
+
after v0.1.2; future versions use the manual trusted-publishing workflow.
|
|
40
|
+
|
|
41
|
+
PR CodeQL results can omit findings outside the reported changes. Required
|
|
42
|
+
**Analyze** checks therefore come from full branch-push/dispatch scans; PR scans
|
|
43
|
+
use distinct names. A clean PR analysis alone cannot authorize release, and the
|
|
44
|
+
post-merge full scan must pass before publishing. The integration harness keeps
|
|
45
|
+
fixture session responses in memory instead of writing an unused response dump.
|
|
46
|
+
|
|
47
|
+
The release-preparation dependency update changes only development declarations
|
|
48
|
+
(`@types/node` and `undici-types`), not production dependencies or runtime authority.
|
|
49
|
+
Install scripts remain disabled. Node 22/24 production-only tests still guard the
|
|
50
|
+
offline CLI contract; Node 26 type declarations alone do not prove that contract.
|
|
51
|
+
|
|
52
|
+
Configuration reads validate the opened descriptor rather than checking a path
|
|
53
|
+
before opening it. No-follow and nonblocking flags reject final symlinks and
|
|
54
|
+
avoid FIFO hangs. Reads are bounded even if a file grows after validation; link
|
|
55
|
+
count, size, mode, and timestamps are rechecked before parsing. This does not
|
|
56
|
+
protect against a hostile owner of the parent directory or remove the documented
|
|
57
|
+
final-check/rename race with an uncooperative external editor.
|
|
58
|
+
|
|
59
|
+
## Release assurance boundary
|
|
60
|
+
|
|
61
|
+
Release checks reject every configured finding, regardless of severity, as well
|
|
62
|
+
as scanner errors, missing output, stale receipts, source mismatches, and missing
|
|
63
|
+
compatibility cases. npm audit covers development, optional, and peer dependencies;
|
|
64
|
+
npm signatures checks registry signatures; Trivy checks the lockfile and repository
|
|
65
|
+
misconfiguration; Semgrep uses pinned OSS language/Actions security rules; Gitleaks
|
|
66
|
+
checks full Git history and current nonignored source. No scan is proof of absence
|
|
67
|
+
of vulnerabilities. This does not certify the host OS or an upstream CLI binary.
|
|
68
|
+
|
|
69
|
+
The repository's `.semgrepignore` overrides default test exclusions: source,
|
|
70
|
+
test harnesses, and fixtures are scanned. Required entrypoint coverage is checked
|
|
71
|
+
explicitly. Only generated output and separately audited dependency code are
|
|
72
|
+
excluded. Downloaded rule fixtures are not project source and must not be scanned
|
|
73
|
+
as if they were part of Lookout.
|
|
74
|
+
|
|
75
|
+
Rule downloads are SHA-256 checked in memory before tar receives the same bytes
|
|
76
|
+
on stdin. The archive is not saved to disk. Each run extracts into a fresh private
|
|
77
|
+
directory, preventing stale rule files from changing the configured scan. A
|
|
78
|
+
checksum mismatch or extraction failure aborts before any success receipt.
|
|
79
|
+
|
|
80
|
+
Actions are SHA-pinned and use standard public runners. Repository access is
|
|
81
|
+
read-only except CodeQL security-result uploads (`security-events: write`);
|
|
82
|
+
CodeQL database uploads are disabled. Its Actions and JavaScript/TypeScript
|
|
83
|
+
analyses reject every SARIF result and warning/error, not just scanner failures.
|
|
84
|
+
Explicit `none`/`note` invocation diagnostics (such as successfully extracted
|
|
85
|
+
files) are informational, not findings. Missing or unknown diagnostic levels and
|
|
86
|
+
recorded exceptions still block, as do findings with any level, even `none`.
|
|
87
|
+
Downloads of
|
|
88
|
+
standalone scanner binaries, the Semgrep image, rule archive, and Node base image
|
|
89
|
+
are digest-pinned. CLI versions are discovered dynamically, and npm integrity binds
|
|
90
|
+
each tested binary. A malicious registry, compromised upstream release, or scanner
|
|
91
|
+
blind spot remains a risk. Dependency overrides cross major versions and require
|
|
92
|
+
behavioral tests, not just a clean audit.
|
|
93
|
+
|
|
94
|
+
Docker test builds need network access to fetch dependencies. Test execution has
|
|
95
|
+
no network, host mounts, Docker socket, elevated capabilities, or personal secrets.
|
|
96
|
+
Fixture provider/tool denials remain mandatory even inside the container. The
|
|
97
|
+
repository and Docker daemon are trusted inputs, not a sandbox for arbitrary
|
|
98
|
+
malicious code. Local receipts are not signed attestations: a user with filesystem
|
|
99
|
+
access can forge them. Exact-revision maintainer review and matching hosted checks
|
|
100
|
+
remain required. No workflow automatically publishes or treats AI review as human
|
|
101
|
+
approval. The approved breaking rename does not claim old ownership blocks;
|
|
102
|
+
restore/unload the previous installation with its own CLI before reinstalling.
|
|
103
|
+
|
|
104
|
+
## Assets and authority
|
|
105
|
+
|
|
106
|
+
Protect global OpenCode configuration, existing access restrictions, credentials,
|
|
107
|
+
and unrelated plugins. The server process already runs with the user's filesystem
|
|
108
|
+
authority. Its authenticated clients can call plugin RPCs. Confirmation protects
|
|
109
|
+
against accidental UI use; it is not a sandbox against a malicious authenticated
|
|
110
|
+
client or another plugin running as the same user.
|
|
111
|
+
|
|
112
|
+
The TUI is not an authority for configuration paths, resource IDs, or management
|
|
113
|
+
eligibility. The server fixes the target path and rechecks the catalog. RPCs are
|
|
114
|
+
not exposed as model tools. No credential APIs are used.
|
|
115
|
+
|
|
116
|
+
## Threats and implemented controls
|
|
117
|
+
|
|
118
|
+
| Threat | Control | Evidence |
|
|
119
|
+
| --- | --- | --- |
|
|
120
|
+
| Wildcard disables or arbitrary file writes | Exact ID validation, fixed config path, fixed resource kinds | Config and controller tests |
|
|
121
|
+
| Erasing external policy | Remove only owned blocks; never add allow rules | Config tests |
|
|
122
|
+
| Replay or stale preview | One-use token, 60-second expiry, configuration hash | Controller tests |
|
|
123
|
+
| Two extension writers | Exclusive file lock and atomic replacement | Controller tests |
|
|
124
|
+
| Partial write or stale lock | Temp-file cleanup; preserve original on pre-rename failure; offline recovery | Controller tests and README |
|
|
125
|
+
| Credential disclosure | Display-field allowlist; no raw API errors or configuration in the catalog | Catalog tests |
|
|
126
|
+
| Terminal control sequences in metadata | Strip control characters and bound display strings | Catalog tests |
|
|
127
|
+
| Loss of re-enable path after filtering | Reconstruct disabled rows from owned entries | Catalog tests |
|
|
128
|
+
| Disabling policy enforcement | Built-in plugins and Lookout are read-only in the UI | Config and catalog tests |
|
|
129
|
+
| Broken UI prevents recovery | External load/unload/status/restore commands; no automatic discovery | Lifecycle tests |
|
|
130
|
+
|
|
131
|
+
The live fixture validates filtering and recovery for all three controllable
|
|
132
|
+
resource kinds. It runs with temporary HOME/XDG directories and denies provider
|
|
133
|
+
and tool use to keep keyboard-test mistakes from starting inference or shell work.
|
|
134
|
+
Terminal logs can contain local paths and session metadata; retained fixtures are
|
|
135
|
+
local debugging artifacts, not shareable evidence. See [Validation](VALIDATION.md)
|
|
136
|
+
for the tested runtime and remaining coverage limits.
|
|
137
|
+
|
|
138
|
+
Plugin descriptions add read-only access to the exact local plugin source from
|
|
139
|
+
the server catalog, or its direct package manifest. File reads are bounded and
|
|
140
|
+
reject non-regular files and final-component symlinks. Plugin source is never
|
|
141
|
+
executed for metadata extraction. Only an explicit description field/tag is
|
|
142
|
+
returned, sanitized for terminal control characters; errors do not expose paths.
|
|
143
|
+
The catalog, not an RPC caller, selects the source path. An installed malicious
|
|
144
|
+
plugin already has the server user's authority; these checks are not a sandbox
|
|
145
|
+
against that user or plugin. Description metadata is display text, not instructions.
|
|
146
|
+
|
|
147
|
+
## Limits and review
|
|
148
|
+
|
|
149
|
+
Public contribution and CI are additional trust boundaries. GitHub Actions run
|
|
150
|
+
untrusted pull-request code without repository write permissions or project
|
|
151
|
+
secrets. Checkout credentials are not persisted, action revisions are pinned, and
|
|
152
|
+
dependency install scripts are disabled. Dependabot proposes updates; it does not
|
|
153
|
+
merge or publish them. Human review must check runtime dependency compatibility.
|
|
154
|
+
Dependency licenses and notices must be reviewed before redistributing bundled
|
|
155
|
+
artifacts; this repository does not publish bundles.
|
|
156
|
+
|
|
157
|
+
The status-dot and MCP-display change affects presentation only. It adds no RPC,
|
|
158
|
+
configuration write, credential access, or trust boundary. Existing MCP
|
|
159
|
+
restrictions remain intact and can still be removed by offline restore. Green
|
|
160
|
+
dots indicate catalog availability, not successful authentication or execution.
|
|
161
|
+
|
|
162
|
+
- Global changes can affect active work in other sessions. The preview warns about
|
|
163
|
+
this; it does not forcibly stop or migrate those sessions.
|
|
164
|
+
- Higher-priority policy can override a local denial. A saved entry is not proof
|
|
165
|
+
that a resource is blocked. The catalog reports when it remains listed.
|
|
166
|
+
- An uncooperative external writer can race the final hash check. Keep manual
|
|
167
|
+
config edits separate from Lookout changes.
|
|
168
|
+
- Loaded skill text cannot be removed from an existing conversation.
|
|
169
|
+
- Server-side plugin status does not certify its terminal component is healthy.
|
|
170
|
+
- Managed provenance is not available for every catalog field. Unrecognized
|
|
171
|
+
sources are read-only instead of being assumed local.
|
|
172
|
+
- Denials are operational configuration, not a sandbox for arbitrary plugin code.
|
|
173
|
+
|
|
174
|
+
The owner of this local installation owns these residual risks. Human review is
|
|
175
|
+
pending; test results are not human approval. Recheck this model when adding
|
|
176
|
+
resource kinds, file targets, credential access, or remote management.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Validation
|
|
2
|
+
|
|
3
|
+
## Packed consumer validation
|
|
4
|
+
|
|
5
|
+
`make check` includes `build:check` and `test:package`. The latter packs the actual
|
|
6
|
+
artifact, installs it outside the checkout with `--omit=dev --ignore-scripts`,
|
|
7
|
+
audits its dependency graph at every severity, and exercises the npm bin and
|
|
8
|
+
offline failure/recovery paths with isolated HOME/XDG directories. It rejects
|
|
9
|
+
accidentally installed SDK/build dependencies. Node 22/24 CI runs this too.
|
|
10
|
+
|
|
11
|
+
The Docker matrix now loads a production-installed tarball from `/consumer`, not
|
|
12
|
+
the harness checkout. The runner packs once into `.build/npm-artifact/` and copies
|
|
13
|
+
those exact bytes into every image; it does not repack with another platform's
|
|
14
|
+
gzip implementation. Each image's tarball must match the local pack's SHA-512
|
|
15
|
+
integrity. The source and package digests are both recorded in compatibility and
|
|
16
|
+
release receipts. Fixture provider/tool denials and both terminal modes remain.
|
|
17
|
+
For a local packed server/TUI case, use:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
LOOKOUT_PACKED_INTEGRATION=1 LOOKOUT_TUI=1 npm run test:package
|
|
21
|
+
LOOKOUT_PACKED_INTEGRATION=1 LOOKOUT_TUI=1 LOOKOUT_NARROW=1 LOOKOUT_THEME=tokyonight npm run test:package
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
These tests prove stable-prefix/path loading. They do not prove direct registry
|
|
25
|
+
entry recovery, npm ownership, OIDC authentication or registry publication.
|
|
26
|
+
|
|
27
|
+
## Repository checks
|
|
28
|
+
|
|
29
|
+
`make check` runs Prettier, local documentation-link checks, blocking ESLint/JSDoc,
|
|
30
|
+
strict TypeScript with checked JavaScript, and Node's test runner. Unit tests
|
|
31
|
+
cover catalog filtering, literal search, description metadata, ownership,
|
|
32
|
+
reversible controls, malformed config, duplicate keys, protected IDs, expired
|
|
33
|
+
and replayed previews, external edits, symlinks, locks, and offline recovery.
|
|
34
|
+
Release tests reject low/unknown findings, scanner errors, incomplete reports,
|
|
35
|
+
stale/source-mismatched evidence, incomplete matrices, and failed commands.
|
|
36
|
+
Workflow tests keep required full CodeQL checks distinct from PR-only checks.
|
|
37
|
+
The terminal fixture asserts that session responses are not saved to disk.
|
|
38
|
+
|
|
39
|
+
## Runtime checks
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
LOOKOUT_TUI=1 node test/integration.mjs
|
|
43
|
+
LOOKOUT_TUI=1 LOOKOUT_NARROW=1 LOOKOUT_THEME=tokyonight node test/integration.mjs
|
|
44
|
+
npm run test:compatibility
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The first two commands use the installed CLI with fresh HOME/XDG fixtures. They
|
|
48
|
+
deny every provider/tool action and never submit a model request. They exercise:
|
|
49
|
+
|
|
50
|
+
- Skill, MCP, and local plugin disable/reload/re-enable and filtered-row recovery.
|
|
51
|
+
- Rejection of protected controls; offline unload/load and RPC disappearance.
|
|
52
|
+
- Carbonfox at 150×52 and Tokyonight at 80×36, search, category/state selection,
|
|
53
|
+
details, cancellation, confirmation, and offline restore; zero inference cost.
|
|
54
|
+
- Native MCP status without duplicate inventory; file-sourced plugin descriptions.
|
|
55
|
+
|
|
56
|
+
After cancelling a preview, the terminal harness checks the reopened catalog's
|
|
57
|
+
full heading rather than requiring a contiguous search placeholder. A retained
|
|
58
|
+
failure showed the renderer updating only changed placeholder cells. A synthetic
|
|
59
|
+
regression covers that output and rejects unrelated dialogs; cancellation must
|
|
60
|
+
still leave zero owned restrictions, and confirmation and offline restore must
|
|
61
|
+
still change and restore the fixture. This test-only correction does not change
|
|
62
|
+
runtime behavior, permissions, or the threat model's trust boundaries.
|
|
63
|
+
|
|
64
|
+
The Docker runner discovers the newest five stable V2+ CLI versions plus the
|
|
65
|
+
highest RC of the nearest unreleased version. It fails if discovery or any test
|
|
66
|
+
fails. Each image installs a CLI artifact with verified npm integrity and locked
|
|
67
|
+
dependencies. Tests run as non-root, without network, capabilities, host mounts,
|
|
68
|
+
credentials, or a Docker socket. Only temporary container storage is writable.
|
|
69
|
+
Both terminal modes run for each target; server/recovery checks run in each mode.
|
|
70
|
+
The approved read-only profile for 2.2.1/2.2.2 requires visible inventory, rejected
|
|
71
|
+
enable/disable/apply RPCs, browsing/details, no changed restrictions, and offline
|
|
72
|
+
load/unload. All other targets require full effective controls. These are distinct
|
|
73
|
+
support claims, not silently skipped test steps.
|
|
74
|
+
Reports bind results to source bytes, CLI integrity, and Docker image IDs.
|
|
75
|
+
|
|
76
|
+
## Evidence and limits
|
|
77
|
+
|
|
78
|
+
Machine-readable evidence is generated under ignored `release-evidence/`; do not
|
|
79
|
+
commit raw scanner logs or terminal fixtures. See [Release policy](RELEASING.md).
|
|
80
|
+
Read the source digest, target, profile, mode, and exit status together. A passing
|
|
81
|
+
probe or an earlier commit's run is not a passing current release matrix.
|
|
82
|
+
|
|
83
|
+
The initial five-version run reproduced an ineffective skill restriction on
|
|
84
|
+
2.2.1. Upstream source confirms 2.2.1/2.2.2 use a different policy action; the
|
|
85
|
+
current `skill.use`/`mcp.use` API starts in 2.2.3. The maintainer chose not to add
|
|
86
|
+
an older-policy adapter and approved testing those hosts against a read-only
|
|
87
|
+
support profile. The original failed control test is not counted as passing;
|
|
88
|
+
fresh profile-specific evidence is required.
|
|
89
|
+
|
|
90
|
+
The old installation is deliberately untouched. Reinstallation is deferred until
|
|
91
|
+
after remote release. Historical pre-rename tests do not establish this revision's
|
|
92
|
+
compatibility. No broader platform/version support is claimed before its matrix
|
|
93
|
+
passes. Linux amd64 hosted results are distinct from local Linux arm64 results.
|
|
94
|
+
|
|
95
|
+
PTY tests are not pixel-level or accessibility audits. Mouse interactions,
|
|
96
|
+
preference persistence, remote-server behavior, and other terminal sizes still
|
|
97
|
+
need human review. Catalog filtering does not revoke instructions already loaded
|
|
98
|
+
in conversations. Catalog availability proves neither provider authentication nor
|
|
99
|
+
quota. A clean scan means only no findings in the configured checks at that time.
|
package/package.json
CHANGED
|
@@ -1,6 +1,105 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-lookout",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "A native-style resource sidebar for OpenCode, with reversible local controls.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/sodejm/opencode-lookout.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/sodejm/opencode-lookout/issues"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/sodejm/opencode-lookout#readme",
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=22"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public",
|
|
19
|
+
"registry": "https://registry.npmjs.org/"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"src/",
|
|
23
|
+
"bin/lookout.mjs",
|
|
24
|
+
"dist/tui.js",
|
|
25
|
+
"docs/*.md",
|
|
26
|
+
"CHANGELOG.md",
|
|
27
|
+
"SECURITY.md",
|
|
28
|
+
"SUPPORT.md",
|
|
29
|
+
"CONTRIBUTING.md",
|
|
30
|
+
"GOVERNANCE.md",
|
|
31
|
+
"CODE_OF_CONDUCT.md"
|
|
32
|
+
],
|
|
33
|
+
"bin": {
|
|
34
|
+
"lookout": "bin/lookout.mjs"
|
|
35
|
+
},
|
|
36
|
+
"type": "module",
|
|
37
|
+
"exports": {
|
|
38
|
+
".": "./src/server.ts",
|
|
39
|
+
"./tui": "./dist/tui.js",
|
|
40
|
+
"./rpc": "./src/rpc.ts"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "node scripts/build-tui.mjs --write",
|
|
44
|
+
"build:check": "node scripts/build-tui.mjs",
|
|
45
|
+
"test": "node --test test/*.test.mjs",
|
|
46
|
+
"test:package": "node test/packed.mjs",
|
|
47
|
+
"typecheck": "tsc --noEmit",
|
|
48
|
+
"lint": "eslint . --max-warnings 0",
|
|
49
|
+
"test:compatibility": "node scripts/compatibility.mjs",
|
|
50
|
+
"security": "node scripts/security.mjs",
|
|
51
|
+
"release:check": "node scripts/release-check.mjs",
|
|
52
|
+
"format:check": "prettier --check 'src/**/*.{mjs,ts,tsx}' 'test/**/*.mjs' 'bin/*.mjs' 'scripts/*.mjs' '*.json' '.github/**/*.yml' '.vscode/*.json' 'eslint.config.mjs' 'opencode-lookout.code-workspace'",
|
|
53
|
+
"test:integration": "node test/integration.mjs",
|
|
54
|
+
"test:tui": "LOOKOUT_TUI=1 node test/integration.mjs",
|
|
55
|
+
"docs:check": "node scripts/check-docs.mjs",
|
|
56
|
+
"check": "npm run format:check && npm run docs:check && npm run lint && npm run typecheck && npm run build:check && npm test && npm run test:package"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"jsonc-parser": "3.3.1"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"@opencode/plugin": ">=2.2.1 <=2.2.6",
|
|
63
|
+
"@opentui/core": "^0.8.0",
|
|
64
|
+
"@opentui/solid": "^0.8.0",
|
|
65
|
+
"solid-js": "^1.9.0"
|
|
66
|
+
},
|
|
67
|
+
"peerDependenciesMeta": {
|
|
68
|
+
"@opencode/plugin": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
71
|
+
"@opentui/core": {
|
|
72
|
+
"optional": true
|
|
73
|
+
},
|
|
74
|
+
"@opentui/solid": {
|
|
75
|
+
"optional": true
|
|
76
|
+
},
|
|
77
|
+
"solid-js": {
|
|
78
|
+
"optional": true
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"overrides": {
|
|
82
|
+
"glob@>=9 <=14.0.1": "14.0.3",
|
|
83
|
+
"minimatch@>=7 <11.0.1": "11.0.4"
|
|
84
|
+
},
|
|
85
|
+
"devDependencies": {
|
|
86
|
+
"@babel/core": "7.29.7",
|
|
87
|
+
"@babel/preset-typescript": "7.27.1",
|
|
88
|
+
"@types/babel__core": "7.20.5",
|
|
89
|
+
"babel-preset-solid": "1.9.16",
|
|
90
|
+
"@eslint/js": "10.0.1",
|
|
91
|
+
"@opencode/client": "2.2.5",
|
|
92
|
+
"@opencode/plugin": "2.2.5",
|
|
93
|
+
"@opencode/theme": "2.2.5",
|
|
94
|
+
"@opentui/core": "0.8.0",
|
|
95
|
+
"@opentui/solid": "0.8.0",
|
|
96
|
+
"@types/node": "26.6.6",
|
|
97
|
+
"eslint": "10.14.0",
|
|
98
|
+
"eslint-plugin-jsdoc": "65.4.3",
|
|
99
|
+
"globals": "17.14.0",
|
|
100
|
+
"prettier": "3.9.10",
|
|
101
|
+
"solid-js": "1.9.16",
|
|
102
|
+
"typescript": "5.9.3",
|
|
103
|
+
"typescript-eslint": "8.72.1"
|
|
104
|
+
}
|
|
105
|
+
}
|