Security, privacy and compliance

Gillish Core's posture, and the ecosystem CRA program it owns.

This page states the real posture, not an aspiration. Every line is either source-cited or marked "not assessed". The honest ladder is met, partial, not assessed, not applicable. A generalised "fully compliant" claim is a legal liability, so it is never written here. Nothing on this page is a certification. This is a working document and it changes as work lands.

Permanent guardrails#

Doctrine, not per-task decisions. Shared across the ecosystem (Core, Cairnstone, Node). They bind every session and every change so the posture cannot drift, and they are written as what not to do, because on a WordPress plugin the common compliance reflexes are the dangerous ones.

GuardrailThe rule
Compliance layeringNo server-level configuration (strict CSP, TLS headers, HSTS) injected from plugin code; a plugin does not own the web server. No .env; configuration is wp-config.php, WordPress options, or the approved GCO_* constants. WordPress core owns auth and password hashing; Core never overrides or shadows it.
Secure by designEvery change keeps PHPStan at level 9 with 0 errors and conforms to the WordPress-Extra phpcs ruleset. Input sanitisation and nonce plus capability checks are mandatory at every entry point. No tree-wide automated fixes; compliance is verified surgically, per unit. No hardcoded secrets.
Honest documentationEvery GDPR, CRA, EAA or WCAG claim is defensible, source-cited, and mapped to a real status. Fabricated or generalised "compliant" statements are never written.

Compliance status#

Honest ladder for the Core shell as it exists today. "Partial" means a real baseline is enforced but a formal end-to-end audit has not been done. "Not assessed" means no claim is made; it is never an implied pass.

StandardStatusBasis
OWASP Top 10PartialThe shell's attack surface is tiny: one nonce+capability-gated AJAX endpoint, two capability-gated read-only admin pages, output escaped via esc_*. PHPStan level 9 / WordPress-Extra phpcs are the enforced bar. No third-party penetration test.
GDPRMet (by scope)The shell collects no telemetry and stores no visitor data. The only persisted datum is the per-user gco_color_scheme admin preference, removed in uninstall.php. No phone-home. Re-assess if Core ever aggregates cross-plugin data.
CCPA / CPRANot applicableCore neither "sells" nor "shares" personal information; it processes no visitor data at all. A functional determination from documented data behaviour, not legal advice.
CRAPartialPhase 2 gap audit complete 2026-05-20 (see §Annex I gap audit). Core's tiny shell surface lands a **higher proportion of met and not applicable rows** than Cairnstone/Node (5 met, 6 partial, 3 N/A in Part I) because several Annex I clauses are trivially satisfied by absence (no visitor data path, no DoS vector, no security-event log needed beyond WP core). Build gates enforced; zero runtime third-party dependencies in the shell. CycloneDX 1.5 SBOM landed (build/sbom-0.1.0.cdx.json; PHP platform only). The ecosystem program is below (§CRA). Phase 3 (declared support period, EU DoC, CE, Annex VII technical documentation) is gated to product launch.
EAA / WCAG 2.2 AAPartialAA is the stated bar. The branding header's interactive controls carry visible :focus-visible indicators and accessible titles; the dashboard grid is semantic HTML. No full third-party WCAG audit.

Data handling#

  • No visitor data. Core has no frontend data path. It runs only on admin screens and the logged-in admin bar.
  • Per-user preference. gco_color_scheme user meta ('light'|'dark'), deleted for all users in uninstall.php.
  • Ecosystem detection. Shared\Ecosystem only calls class_exists() on sibling orchestrator classes; it reads no data from siblings and transmits nothing.
  • No phone-home. No activation telemetry, no usage stats. This documentation site self-hosts its webfont; no third-party font CDN call.

Security measures#

  • Static analysis. PHPStan level 9, 0 errors, run after every PHP edit batch; new errors are regressions.
  • Lint. WordPress-Extra + PHPCompatibility + PHPCSExtra phpcs.
  • Input handling. The single writable endpoint sanitises and nonce+capability-verifies before touching user meta.
  • Output. Every output goes through the appropriate esc_* helper; the one exception (the shipped SVG icon) is commented as author-controlled.
  • Headers / TLS. Deliberately not handled by the plugin (guardrail 1: the host/server owns this layer).
  • Secrets. No hardcoded secrets in the plugin codebase; configuration is wp-config.php / WordPress options / approved constants, and cryptographic keying derives from WordPress salts, never inline literals.

The ecosystem CRA program#

The EU Cyber Resilience Act (Regulation (EU) 2024/2847) treats the studio as a "manufacturer" of "products with digital elements" for every commercially-supplied Gillish plugin. There is one Core-owned program over the whole ecosystem, not a per-plugin scramble, because most Gillish tools are pre-launch or parked: this is a design-in-now, certify-at-launch program. This is not a compliance claim. Honest status:

StatusPart of the programDetail
Settled, in-house Scope: who and what is in scope Settled in-house 2026-05-18 (no external counsel; residual legal risk explicitly owner-accepted). Working determination: every Gillish PDE is "default" category, so internal self-assessment, no notified body. Never supports a "compliant" claim.
Operational Coordinated vulnerability disclosure & security contact Operational since 2026-05-19: a monitored security@gillish.com, a public CVD policy, and an RFC 9116 security.txt. See the Reporting a vulnerability section below.
Documented, not operational Art. 14 regulatory reporting (24 h / 72 h / 14-day) The runbook and clock exist; the reporting channel (ENISA single reporting platform registration plus the coordinating-CSIRT determination for a Norwegian or EEA manufacturer) is an owner action still pending. Hard deadline: 11 Sep 2026.
Deferred by design SBOM, technical documentation, EU DoC + CE, declared support period SBOM landed in Phase 2 (2026-05-20) for Cairnstone, Node, and Core (CycloneDX 1.5 per tool, in each repo's build/); Mesh and Feed inherit the template at-resume. Annex VII technical documentation, EU DoC + CE marking, and the declared support period remain Phase 3, per-tool work folded into each tool's launch gate. Backstop: 11 Dec 2027 (no non-conforming product on the EU market). No Gillish tool has launched yet.
Satisfied, undocumented Existing engineering posture (Annex I Part I) Largely already satisfied by house convention (minimal attack surface, no runtime dependencies, strict static-analysis gate, data minimisation). Documented in Phase 2 (2026-05-20): per-clause Annex I gap audits with cited mechanisms now live for Cairnstone, Node, and Core (see each tool's plans/security.md §"Annex I gap audit" and the mirrored public security pages). Mesh and Feed inherit the template at-resume. The cite-your-mechanism discipline keeps met honest, most rows are partial pending formal per-unit audits.

The honest one-line status: scope is settled in-house; the disclosure-intake capability is operational; the Art. 14 reporting channel and all per-tool conformity are pending and deliberately deferred to launch. The phrase "Gillish is CRA-compliant" is never written, because it cannot yet be true.

Reporting a vulnerability#

There is one shared Gillish Coordinated Vulnerability Disclosure (CVD) policy, studio-wide, governing every product (Node, Cairnstone, Feed, Mesh, Core; free and Pro alike). Core owns the canonical policy.

The full policy (scope, safe harbour, the best-effort SLA for a one-person studio with no bug bounty, and the disclosure window) is published in full here: the Gillish CVD policy. It is not duplicated on this page on purpose: one canonical rendering, never a fork. Actively-exploited vulnerabilities and severe security incidents additionally follow the studio's CRA Article 14 regulatory reporting workflow (status above).

Annex I gap audit (Phase 2)#

Phase 2 of the Core-owned CRA program: per-clause mapping of every Annex I requirement to Core's concrete mechanism, with a mandatory citation per row. Completed 2026-05-20 against the clause structure verified the same day from the consolidated OJ text (Gillish Core plans/CRA-PHASE2-PLAN.md §3.1). The status ladder is met / partial / not applicable / not assessed. No row may read met without an exercisable cited mechanism. Core's tiny shell surface means several clauses are honestly met or not applicable because they're trivially satisfied (or do not arise) for a plugin with no visitor data path, no DoS vector, and a 3-entry-point attack surface, the doctrine working, not status inflation.

Annex I Part I: cybersecurity requirements relating to product properties

ClauseStatusConcrete mechanism (cited)
(1) Umbrella, appropriate cybersecurity based on risksPartialPermanent guardrails (§Permanent guardrails above) bind every change; build gates (composer test / stan / lint) enforced. No formal documented risk assessment per CRA Art. 13 yet, Phase 3 deliverable.
(2)(a) No known exploitable vulnerabilitiesPartialPHPStan level 9, 0 errors; WordPress-Extra + PHPCompatibility + PHPCSExtra phpcs; Plugin Check release gate. The shell's attack surface is tiny (3 entry points). No third-party penetration test; no CVE recorded against Core.
(2)(b) Secure-by-default + resettablePartialSafe defaults are wp.org-gate-mandated. Resettable state via uninstall.php, unconditional removal of the one persisted datum (gco_color_scheme user meta) on plugin delete; no opt-in toggle (Core has no analytics history to preserve). No per-surface formal review of default values conducted.
(2)(c) Vulnerabilities addressable via security updatesMetWordPress core's signed update channel (readme.txt Stable tag, wp.org distribution); standard transport guarantees. No obstruction to updates.
(2)(d) Protection from unauthorised accessPartialOne writable entry point: Admin\ColorSchemeAjax::handle_set() with check_ajax_referer + current_user_can. Two read-only admin pages, capability-gated. Single grep-able hook registry (src/Plugin.php, 6 registrations). No REST endpoints. The entire entry-point inventory fits in one sentence, but per-call formal audit not signed off.
(2)(e) Confidentiality of stored / transmitted / processed dataNot applicableThe shell processes no visitor data, no PII, and no sensitive information. The only persisted datum is the per-user gco_color_scheme admin preference ('light' | 'dark'), by design admin-visible and not sensitive; there is nothing requiring confidentiality protection.
(2)(f) Integrity of data, commands, programs, configurationPartialdeclare(strict_types=1) per file; nonce + capability verification on the single writable endpoint; outputs escaped via esc_* helpers at the boundary (the shipped SVG icon is the one author-controlled exception, commented). No per-call-site formal audit.
(2)(g) Data minimisationMetCore collects nothing beyond what is necessary for the one capability it ships (per-user light/dark theme preference). No telemetry, no usage stats, no visitor data. By scope, this is genuinely satisfied.
(2)(h) Availability + DoS mitigationNot applicableCore is a WordPress plugin admin shell; availability sits in the site owner's hosting / WP-core layer. The shell introduces no external network calls, no scheduled jobs, no expensive operations. No DoS vectors introduced.
(2)(i) Minimise negative impact on other servicesMetZero frontend JS / CSS by default, Core runs only on admin screens. No external network calls at runtime. Shared\Ecosystem only calls class_exists() on sibling orchestrator classes, reads no data from siblings, transmits nothing. Core's runtime is process-local on the WP host.
(2)(j) Limit attack surfaces, incl. external interfacesMetThe entire writable attack surface is documented in one sentence: one nonce + capability-gated AJAX endpoint (Admin\ColorSchemeAjax::handle_set()) + two capability-gated read-only admin pages. No REST endpoints, no frontend entry, no scheduled jobs. Single hook registry in src/Plugin.php (6 registrations). This is the formal external-interface inventory, small enough to enumerate exhaustively in this row.
(2)(k) Reduce impact of incidents (exploitation mitigation)Partialdeclare(strict_types=1) per file; PHPStan level 9 catches whole classes of latent type-confusion bugs; WordPress / host layer carries process-level mitigations. No formal exploit-mitigation features beyond established WP best-practice.
(2)(l) Security-related information recording / monitoringNot applicableThe shell has no security events to record: no auth surface, no data flow, no transactions of consequence. WordPress core handles authentication-event logging through its standard mechanisms; Core neither adds nor needs an additional security-event log.
(2)(m) Secure permanent removal of data and settingsMetuninstall.php provides unconditional destructive cleanup of the only persisted datum (gco_color_scheme user meta deleted for all users on plugin delete). No opt-in toggle, no "keep data" default, the shell has no analytics history to preserve, so removal is straightforward and complete by default.

Annex I Part II: vulnerability-handling requirements

ClauseStatusConcrete mechanism (cited)
(1) SBOM in a commonly used machine-readable formatMet (this commit)CycloneDX 1.5 JSON at build/sbom-0.1.0.cdx.json; lists the PHP platform only, honestly near-empty rather than fabricated breadth (Core has zero Composer runtime deps and no vendored runtime third-party). Regenerates on version bump.
(2) Address vulnerabilities without delayMetEcosystem CVD intake operational; security@gillish.com watched via Thunderbird IMAP (2026-05-19); standard wp.org update channel for rapid distribution.
(3) Effective and regular tests and reviewsPartialComposer scripts test / stan / lint (PHPUnit + PHPStan level 9 + WordPress-Extra phpcs + PHPCompatibility); Plugin Check release gate; runs after every PHP edit batch. Automated dependency-CVE audit, in-session pre-push gate: the agent runs composer audit --format=plain against the resolved lockfile before every git push origin main on Core / Cairnstone / Node / MCP; an open Packagist advisory holds the push until resolved. The gate moved from a GitHub-Actions workflow to an in-session pre-push step on 2026-05-24 alongside the broader auto-deploy retirement; same zero-tolerance threshold throughout. Feed and Mesh inherit when their toolchains stand up. No formal regular cadence for human-driven code review beyond per-change.
(4) Public disclosure of fixed vulnerabilitiesPartialCVD policy commits to structured public advisories (readme.txt changelog + docs site); format committed, channel live. No vulnerabilities have been fixed and disclosed yet, first execution awaits the first real report.
(5) Coordinated vulnerability disclosure policyMetLive at html.gillish.com/mesh/security/#cvd; canonical source gillish-core/plans/CRA-CVD-POLICY.md; security.txt published at /.well-known/security.txt (RFC 9116). Core owns the ecosystem CRA program, this is the canonical policy.
(6) Facilitate sharing of vulnerability informationMetMonitored security@gillish.com mailbox + security.txt Contact line + the public CVD policy, three discoverable paths to a working contact.
(7) Secure update distributionMetWordPress core's signed update channel (wp.org Stable tag); standard transport guarantees.
(8) Free + timely security updates within declared support periodPartial, declared support period set in Phase 3CVD policy commits to free security updates within the support period; the support-period number itself is a Phase 3 deliverable. Proposed default 5 years from a version's release. Row moves to met when the support period lands in readme.txt and is declared in the EU Declaration of Conformity.

Summary. Part I, 14 clauses: 5 met, 6 partial, 3 not applicable. Part II, 8 clauses: 5 met, 3 partial. Core has the highest met count and not applicable count of any Gillish tool, because its tiny shell surface trivially satisfies clauses around confidentiality, DoS, and security-event recording (no data, no DoS vector, no events). met and not applicable rows are honestly defensible per the cite-your-mechanism discipline, not inflated.

Not yet assessed (honest gaps)#

  • SBOM generation and an automated dependency-vulnerability audit (CRA): an ecosystem-wide gap, not Core-specific, and the next substantive program step.
  • Full third-party WCAG 2.2 AA audit across every surface.
  • A formal secure-development-lifecycle statement for CRA beyond the build gates already in place.
  • The CRA Art. 14 reporting channel (ENISA platform + coordinating CSIRT for a Norwegian/EEA manufacturer, including a possible authorised-representative obligation): the single biggest open question, owner-tracked.

Status fields move only with a concrete cited mechanism. Stale status is worse than no status; input on any of this goes to the maintainer via the contact above.