Back to Portfolio
Product security case study

Securing Planacle: collaborative data and bounded AI planning

A source-backed security review of a real-time planning product where identity, shared state, ranked voting, privileged Cloud Functions, and Gemini all cross distinct trust boundaries.

Review: 23 August 2026Scope: event lifecycle, Firestore and Storage rules, voting, and Genkit planningMethod: source review + focused tests

Security thesis

The interface is not the trust boundary.

Planacle combines Firebase Authentication, direct Firestore access, authenticated callable functions, external location data, and a Gemini planning pipeline. The browser is therefore a participant in the system—not its security boundary.

I traced creation, joining, preferences, ballots, winner resolution, deletion, public sharing, and AI invocation to the rule or server check that actually enforces each decision. Client-side guards were treated only as user-experience controls.

Architecture and trust boundaries

01

Firebase Authentication establishes caller identity; Firestore rules constrain direct reads and a deliberately small set of client writes.

02

Privileged and concurrency-sensitive operations move into authenticated Cloud Functions that re-check membership, role, bounds, and request budgets.

03

Event joining and capacity changes execute transactionally so concurrent callers cannot bypass limits through a race condition.

04

The planning pipeline treats preferences and provider content as untrusted before Gemini use, then validates and scrubs generated output.

05

Public sharing uses a separate server-generated projection containing presentation fields rather than exposing the private event document.

Verified controls

Event creation

Only authenticated, non-pure-anonymous users may create events, and the stored host identity must match the caller.

Joining

Client participant creation is denied. A callable authenticates, validates bounded input, rate-limits attempts, assigns roles server-side, enforces capacity transactionally, and rejects final-phase joins.

Participant privacy

Availability summaries and activity data remain participant-only and server-written where appropriate.

Ballot integrity

Participants may update only their own ballot fields; ranked lists are capped and voting is gated by event phase and server-time deadline.

Winner resolution

Tallying and tie resolution are server-side, host-triggered, rate-limited, and validated against live candidates and recorded co-winners.

Destructive actions

Client deletion is denied. The callable authenticates the caller, verifies stored host ownership, applies a request budget, and recursively deletes the event tree.

AI authorization

The planning wrapper rejects unauthenticated users and verifies that the caller has a participant record for the requested event.

AI consumption

Per-user quotas, concurrency caps, flow and narrative timeouts, turn limits, token caps, sanitisation, Zod schemas, and explicit model-safety settings bound execution.

Threat scenarios

Threat

A guest self-assigns the host role

Participant creation is server-only. The join function derives the effective role from the event’s stored host identity.

Blocked by server-owned role assignment

Threat

Concurrent joiners exceed event capacity

Event state, participant existence, and count are evaluated inside one Firestore transaction before writes commit.

Bounded transactionally

Threat

A participant overwrites another ballot

Direct ballot updates require the authenticated UID to match the participant path and restrict the writable fields.

Blocked by Firestore rules

Threat

An authenticated outsider invokes AI for another event

The callable verifies the caller’s participant document before starting the planning flow, with quotas and instance caps behind it.

Blocked in the production path

Validation evidence

94

focused root rule and client-service tests passed

150

focused Cloud Function and planning tests passed

0

new Critical or High findings identified by the review

1 known

planning-behaviour regression remained visible rather than being presented as a clean run

Residual risks and limitations

Avatar upload constraints

High · tracked

Add authoritative file-size, image MIME-type, and canonical-path checks alongside client validation.

Event-create field surface

Medium

Move creation behind a callable or strictly allowlist initial fields, types, and sizes in Firestore rules.

Preference payload bounds

Medium

Add item-count, string-length, numeric-range, and location-marker caps to shared and server schemas.

Pre-join capability link

Low · accepted

The unguessable event ID currently acts as a capability before generation. Use a restricted pre-join projection if future data requires stronger confidentiality.

This public version excludes secrets, production identifiers, real user data, internal prompt details, and actionable exploit reproduction steps.

View the Planacle product case study