Crosscutting Concept: Security

1. Threat Model

Assets Protected

Asset Description Value

Config file integrity

opencode-semantic-anchors.yaml defines steering rules

High

Session state integrity

toolCallCount, overrideCount, active role

Medium

Plugin execution integrity

Plugin must enforce rules correctly

High

User’s source code

Plugin can read tool arguments (toolName, args)

High

Threats

Threat Impact Likelihood Mitigation

Config tampering — attacker modifies YAML to disable enforcement

Steering bypass

Low (local file)

File permissions (user-owned); config hash verification (v2)

Config injection — attacker injects YAML with malicious trigger patterns

Denial of service

Low

Zod schema validation rejects unknown fields

Memory disclosure — session state leaked via crash dump

Override count disclosure

Very low

In-memory only, no persistent storage

Dependency compromise — malicious npm package

Full compromise

Low

Lock file, npm audit, Renovate, supply chain monitoring

Plugin hook bypass — opencode bug allows bypassing hook

Enforcement bypass

Low

Fail-open as fallback; opencode SDK issues tracked

PII in tool arguments — user accidentally includes secrets in file paths

Information disclosure

Medium

Plugin does NOT log tool arguments — only toolName

What the plugin does NOT handle

Security domain Handled by Rationale

Authentication

opencode itself

Plugin does not authenticate users

Authorization

opencode permission system

Plugin uses edit permission for config-reload

Network security

User’s environment

Plugin has zero external HTTP calls

Secrets management

age, 1Password, etc.

Plugin explicitly excludes secrets

File system permissions

OS

Plugin reads YAML from user-owned config directory

Source Anchor (Quelle): Architecture Constraints (02-architecture-constraints.md): "No secrets — Plugin never handles passwords, tokens, or API keys. No integration with age or other secret stores." — Section 2, Technical Constraints.

2. Input Validation

YAML Config Validation

All configuration is validated against a Zod schema before loading:

Validation Method Rejects

Contract ID format

.min(1)

Empty IDs

Mode

z.enum(['BLOCK', 'WARN'])

Invalid modes

Trigger type

z.enum(['tool', 'message', 'state'])

Unknown types

Trigger pattern

String regex validation

Invalid patterns

maxOverrides

z.number().default(3)

Non-numeric values

Unknown fields

Zod .strict() or passthrough with warning

Configurable

// From config/schema.ts — structural validation
export const StructuralCouplingContractSchema = z.object({
  id: z.string().min(1),
  mode: z.enum(['BLOCK', 'WARN']),
  description: z.string(),
  anchorRefs: z.array(z.string()).optional(),
  triggers: z.array(TriggerSpecSchema).min(1),
  maxOverrides: z.number().default(3),
})

Source Anchor (Quelle): Zod documentation: https://zod.dev/. Zod .strict() rejects unknown keys; .passthrough() allows them with warnings.

Tool Input Validation

Custom tools (anchor-bypass, anchor-status, anchor-config-reload) validate their own parameters:

Tool Parameter Validation

anchor-bypass

reason: string

Required, non-empty

anchor-status

None

—

anchor-config-reload

None

Checks edit permission

3. Logging and PII

Logging Policy

Logged Not logged Rationale

Tool name (e.g., "edit", "write")

Tool arguments (file paths, content)

Arguments may contain proprietary code or secrets

Contract ID (e.g., "step-confirmation")

User identity or agent name

PII minimization (GDPR Art. 5(1)(c))

Verdict (allow/block/warn)

IP addresses, hostnames

Not needed for enforcement

Override count + reason

Full conversation history

Reasons are user-provided, brief strings

Timestamp (session-relative)

Absolute timestamps or timezone

Avoids session reconstruction

Source Anchor (Quelle): GDPR Article 5(1)(c) — Data minimisation: "Personal data shall be adequate, relevant and limited to what is necessary in relation to the purposes for which they are processed."

Log Storage

  • Logs are in-memory only during the session

  • Not persisted to disk by the plugin

  • Lost on opencode restart

  • opencode itself may log plugin interactions — that is outside the plugin’s control

ISO 27001: Data Leakage Prevention (A.8.12)

The plugin ensures no data exfiltration by: - Zero external HTTP calls (architectural constraint) - No file writes beyond reading its own config YAML - No network connections of any kind - No telemetry — the plugin does not phone home

4. Supply Chain Security

Dependency Verification

Measure Status Implementation

Lock file

✅ Required

package-lock.json committed to repository

Integrity check

✅ npm built-in

npm install verifies SHA-512 integrity from registry

Signature verification

✅ npm v10+

npm audit signatures verifies package provenance

SBOM

🔄 Planned (v1.1)

cyclonedx-bom or npm sbom in CI

Source Anchor (Quelle): npm registry integrity: https://docs.npmjs.com/about-registry-integrity-and-signatures. "Packages in the npm registry include integrity checksums (SHA-512) and optionally package signatures."

Vulnerability Scanning Cadence

Scan Frequency Tool Action on Finding

Runtime dependencies

Weekly

Renovate + npm audit

Patch within SLA

Dev dependencies

Weekly

Renovate + npm audit

Automerge patch/minor

Transitive dependencies

Weekly

Renovate (via npm audit)

Inherited from direct deps

Full audit

Pre-release

npm audit --audit-level=high

Block release if high/critical

5. Secure Development

Developer Workstation Security

Practice Standard

2FA on GitHub

Required for all maintainers

Signed commits

GPG or SSH commit signing

No secrets in code

.env files are excluded; age for local secrets

Dependency updates

Renovate bot, never manual npm install --save without review

Code Review Requirements

Change type Reviewer Must include

Bug fix

1 maintainer

Test case reproducing the bug

New feature

2 maintainers

Tests, CHANGELOG entry, anchor documentation

Config schema change

2 maintainers

Migration guide, Zod schema update

Security fix

2 maintainers

GH Security Advisory reference

Testing for Security

Test type Covers Tool

Unit tests

Enforcement logic, edge cases

vitest

Input validation

Malformed YAML, injection attempts

vitest + Zod

Hook error handling

Fail-open behavior, crash recovery

vitest with mock

Supply chain

Dependency vulnerabilities

npm audit + Renovate

6. Incident Response

Security Incident Handling

For the plugin’s own code (not for opencode or user systems):

  1. Report via GitHub Security Advisory or email to security@semantic-anchors.dev

  2. Triage within 48 hours — determine severity and affected versions

  3. Fix developed in private fork

  4. Patch released as PATCH version bump

  5. Disclosure — CVE + GH Advisory published after patch is available

  6. Post-mortem within 14 days

Expected-Use Security Boundaries

The plugin operates within opencode’s sandbox: - Plugin code runs in the same Node.js process as opencode - Plugin has access to: tool names, tool arguments (passed by opencode), file system (config read) - Plugin does NOT have access to: network, user credentials, other process memory - If opencode itself is compromised, the plugin cannot provide security guarantees

Boundary Anchor (Scope-Grenze): Das Plugin kann keine Sicherheitsgarantien geben, wenn opencode selbst kompromittiert ist. Es ist ein runtime steering tool, kein security enforcement layer. Die Sicherheit des Gesamtsystems hängt von der Sicherheit der opencode-Installation ab.