Internal Audit Report — 2026 Q2
| Field | Value |
|---|---|
| Document ID | IA-2026-Q2 |
| Audit period | 2026-03-20 (Sweep 1) and 2026-03-20 (Sweep 2) |
| Report effective date | 2026-04-30 |
| Auditor | Peter Sterkenburg (Founder & operator), assisted by Anthropic Claude Code (LLM-driven static-review tool) |
| Audit standard | ISO/IEC 27001:2022 clause 9.2 (Internal audit) |
| Root scope reference | ISMS scope statement §2 |
§1 Audit objective
Assess whether the PortalPilot information security management system (in-scope assets per the public scope statement §2) operates effectively against:
- The control objectives of ISO/IEC 27001:2022 Annex A (when published in the upcoming compliance pack release).
- The five SOC 2 Trust Service Criteria as documented in the equivalence map.
- The HubSpot Marketplace pre-submission security baseline (driver for the original Sweep 1 cadence).
- The customer-facing DPA commitments, particularly the 72-hour breach-notification commitment and the sub-processor-list reconciliation.
The audit's secondary objective is to surface defects in the agent-security model (Claude Code + Mistral second-opinion), since the agent itself is part of the in-scope security boundary.
§2 Scope
The audit covered the entirety of the in-scope-assets section of the public scope statement §2, namely:
- §2.1 Production application — all React routes, all Supabase Edge Functions, all Postgres schemas + RLS policies, the customer-facing authentication service.
- §2.2 Customer data — every PII-bearing column in every customer table, traced through the full data flow (HubSpot OAuth → edge function → Postgres → frontend).
- §2.3 Operational infrastructure — the production VM (Hetzner Cloud Finland), the self-hosted Supabase stack, the Coolify orchestration plane, the Plausible analytics instance, the Traefik reverse proxy.
- §2.4 Sub-processors — the four sub-processors listed in the public DPA Annex I (Hetzner, Mistral, Mollie, Lettermint).
- §2.5 Engineering tooling — the four-layer agent-security model and its known limitations.
Out-of-scope (per the public scope statement §3): the founder's development laptop, HubSpot Inc. as a vendor, customer-side HubSpot portal data at rest in HubSpot.
§3 Methodology
Approach
Adversarial review by the founder, augmented by Claude Code (LLM-driven static-review tool), proceeded domain-by-domain with an attacker-perspective lens. For each domain the methodology was:
- Code-path enumeration — list every public-reachable code path (React route, edge function, public RPC).
- Auth + ownership verification — for each path, verify that authentication is enforced and that data-ownership checks gate every read/write of customer-scoped data.
- Input-validation review — verify that every parameter accepted from the network is type-checked, range-checked, and denied-by-default for unrecognised shapes.
- Error-handling review — verify that errors return generic messages to the client; full diagnostic detail goes only to the structured log.
- Side-channel review — examine logs, audit trails, RPC error messages, and HTTP response headers for unintended information disclosure.
Sweep 2 added a fix-verification pass over Sweep 1's HIGH findings, plus extension into payments, CI pipeline, enterprise readiness, and the agent-security model itself.
Limitations of the methodology
- No external penetration test — all findings come from static code review, manual probe-and-observe testing, and LLM-assisted review. Live exploitation is not part of the methodology.
- No formal threat-modelling tool — threats were enumerated narratively per domain, not via a STRIDE / PASTA / LINDDUN tool.
- LLM-assisted review caveat — Claude Code (Opus-tier) was used as a force-multiplier for code reading, finding enumeration, and remediation-plan drafting. Empirical review of Mistral's chunk-review output during recent compliance-pack authoring shows hallucination rates of 31–86 % on individual findings, so every LLM-flagged item was empirically validated by the founder before action. Claude Code itself (Opus tier) had a measurably lower error rate but was still subject to the same per-finding validation discipline.
§4 Findings register — summary
Statistics across both sweeps
| Severity | Sweep 1 (2026-03-20) | Sweep 2 (2026-03-20) | Combined | Status (Closed / Open / Accepted) |
|---|---|---|---|---|
| Critical | 0 | 0 | 0 | n/a |
| High | 7 | 4 | 11 | 11 / 0 / 0 |
| Medium | 15 | 14 | 29 | 25 / 4 / 0 |
| Low | 8 | 15 | 23 | 18 / 5 / 0 |
| Info | 5 | 3 | 8 | 0 / 0 / 8 (informational, no closure required) |
| Total | 35 | 36 | 71 | 54 / 9 / 8 |
All eleven HIGH findings (7 from Sweep 1 + 4 from Sweep 2) closed before pre-marketplace submission. All Sweep-1 HIGH closures were re-verified in Sweep 2 with no regressions.
Top findings — closure detail
The five highest-impact findings and their closure path:
| Finding | Severity | Annex A control affected | Closure |
|---|---|---|---|
Missing portal-ownership checks in 4 edge functions (workflows-analyze, spot-check-accounts, generate-executive-summary, generate-remediation-playbook) — any authenticated user could access another tenant's HubSpot data |
High | A.5.15, A.8.2, A.8.3 | Closed — requirePortalAccess middleware added to all 4 edge functions; 26-edge-function audit confirmed coverage |
| No IP-level rate limiting on the login endpoint — credential-stuffing across many email addresses bypassed per-email lockout | High | A.5.15, A.8.5, A.8.20 | Closed — IP-level rate limiting via checkRateLimitByUser; fail-closed on DB error |
Incomplete GDPR data-deletion cascade — right to erasure left orphan rows in 49 tables |
High | A.5.34, A.8.10, A.8.11 | Closed — comprehensive deletion now covers 39 portal-keyed tables + 10 user-keyed tables |
edge_function_logs RLS allowed any authenticated user to read all rows |
High | A.5.15, A.8.3, A.8.16 | Closed — RLS now restricts to has_role(auth.uid(), 'admin'::app_role) |
Mollie upgradeCredit server-trust bypass — a client-supplied credit value was trusted server-side, enabling discounted subscriptions via crafted requests |
High | A.5.15, A.8.27, A.8.32 | Closed — server-side server-trust check added; client-supplied credit ignored |
Open items at this report's effective date
Nine items (4 Medium + 5 Low) remain open. None are HIGH-severity. Status snapshot:
| Open item | Severity | Annex A control | Reason still open |
|---|---|---|---|
| Backups not GPG-encrypted before transit to Hetzner snapshot store | Medium | A.5.30, A.8.13, A.8.24 | Hetzner snapshot is on the same provider; transit is intra-Hetzner-network; full disk LUKS already in place. Fix planned in Phase 2 alongside off-site backup work |
| CI runner shares production server | Medium | A.5.32, A.8.32 | Acknowledged trade-off; CI is constrained to read-only operations + scoped deploy keys; Phase 2 to migrate CI to a separate Hetzner project |
track-ai-usage portal enumeration possible via crafted UUID |
Medium | A.5.15, A.8.3 | Mitigated by rate limiting on the function; full ownership-gate fix queued |
| Service-worker cache poisoning on logout | Medium | A.5.15, A.8.27 | UI workaround in place (forced reload on logout); architectural fix queued |
| Anon-key rotation cadence not formalised | Low | A.5.16, A.8.5 | Procedure documented; cadence to be set in Q3 2026 |
| Mixed Deno-import versions in three edge functions | Low | A.8.30, A.8.32 | Cosmetic; tracked in dependency-cleanup backlog |
Two npm dependencies on ^x.y.z rather than exact pin |
Low | A.5.32, A.8.30 | Risk-accepted; package-lock.json provides effective pinning |
| Encryption-key entropy spec not documented | Low | A.5.17, A.8.24 | Documented internally during this audit period; pending public-doc reflection |
CSP default-src could be tightened to self |
Low | A.8.27, A.8.28 | Cosmetic; queued in front-end hardening backlog |
The full per-finding evidence and remediation rationale is held in the audit archive, available to enterprise procurement reviewers under NDA.
§5 Annex A control coverage tested
The audit tested controls across all four Annex A themes. Coverage by theme:
| Theme | Controls touched (approx.) | Coverage characterisation |
|---|---|---|
| A.5 Organizational (37 controls) | ~22 controls touched | Auth, access control, classification, supplier relationships, incident management — comprehensively tested. Strategic / governance controls (e.g. A.5.1 policies, A.5.4 management direction) were attested rather than empirically tested |
| A.6 People (8 controls) | 3 controls touched (A.6.3 awareness, A.6.4 disciplinary, A.6.7 remote working) | Sole-trader entity makes most A.6 controls trivial-by-structure; reviewed for accuracy of structural attestation |
| A.7 Physical (14 controls) | 0 controls touched empirically | All A.7 controls transferred-to-supplier (Hetzner ISO 27001 / 27017 / 27018); reviewed by reading the Hetzner certification scope |
| A.8 Technological (34 controls) | ~28 controls touched | Endpoint, network, application, cryptography, vulnerability management, change management — comprehensively tested |
Summary coverage: approximately 53 of 93 controls (~57 %) were empirically tested. The remainder were either attested-by-structure (A.6 controls under sole-trader form), transferred-to-supplier (A.7 controls under Hetzner certifications), or governance-tier (A.5.1–A.5.4 attested rather than tested).
§6 Conclusions
The PortalPilot information security management system is operating effectively. All eleven HIGH findings from Sweep 1 + Sweep 2 are closed and re-verified. Nine open items, all Medium-or-Low, are tracked in the engineering backlog with documented rationale or remediation timelines. No CRITICAL findings were surfaced in either sweep.
The most material structural risks — single-server topology, single-founder concentration, no third-party SOC 2 attestation — are inherent to NordScope's current sole-trader stage and are explicitly acknowledged in the public ISMS scope statement (§3 out-of-scope) + the SOC 2 equivalence map (Compensating measures column).
The methodology itself has known limitations (no external pen test, no formal threat-modelling tool, LLM-assisted review with empirical-validation discipline). Phase 2 third-party attestation is deferred until enterprise-customer demand justifies the audit cost.
§7 Next audit due
| Audit | Cadence | Due | Owner |
|---|---|---|---|
| Q3 2026 internal audit | Quarterly | 2026-07-15 | Founder |
| Q4 2026 internal audit | Quarterly | 2026-10-15 | Founder |
| Q1 2027 internal audit (anniversary review) | Quarterly + annual | 2027-01-15 | Founder |
| Annual scope-statement review | Annual | 2027-04-30 | Founder |
The Q3 2026 audit will explicitly cover:
- Verification that this report's nine open items are either closed or have a documented status update.
- Coverage of any Annex A control rows added or modified since the Q2 audit.
- Re-test of the agent-security model in light of any changes to Claude Code, Mistral, or the project's hook configuration.
- Sub-processor list reconciliation against the public DPA Annex I.
§8 Auditor declaration
I, Peter Sterkenburg, in my capacity as founder and sole operator of NordScope, performed the internal audit described in this report between 2026-03-20 and 2026-04-30. The audit was assisted by Anthropic Claude Code (an LLM-driven static-review tool); every LLM-flagged finding was empirically validated by the founder before being recorded as a real finding in the register, and every claimed closure was independently re-verified by the founder in Sweep 2.
This is an internal audit performed under ISO/IEC 27001:2022 clause 9.2. It is not a third-party attestation, certification, or examination by a CPA firm. Procurement reviewers should treat this report as alignment evidence, in the same family as the SOC 2 equivalence map and the public ISMS scope statement.
— Peter Sterkenburg, 2026-04-30
Document history
| Version | Date | Author | Change |
|---|---|---|---|
| v1.0 | 2026-04-30 | Peter Sterkenburg | Initial publication; covers Sweep 1 (2026-03-20) + Sweep 2 (2026-03-20) re-framed into ISO clause 9.2 form |
Linked from NordScope's internal security runbook (NDA counterpart) under the "Annual security tasks" section. The full per-finding evidence base is held in NordScope's audit archive and made available to enterprise procurement reviewers on request.