Skip to content

BlobTemplateStore interpolates tenantId/environment into blob paths without path validation #59

Description

@patoperpetua

Found while writing the security boundary tests for #41.

Observation

BlobTemplateStore.load builds the blob name by string interpolation:

const basePath = `tenants/${tenantId}/${environment}/templates/${templateKey}`;

templateKey is validated against an allowlist before any storage access, but tenantId and environment are not. The publisher package already has the matching guards (assertSafeTenantId, assertSafeEnvironment in packages/post-kit-publisher/src/path-safety.ts); the API-side store does not use them.

Impact

Azure Blob Storage has a flat namespace, so this is not filesystem path traversal — the value is sent as a literal part of the blob name. The concrete risk is that the per-tenant prefix is only a naming convention: a tenantId or environment containing / silently produces a blob name outside the documented tenants/{tenant}/{env}/templates/{key}/ shape, which is also the shape any prefix-scoped SAS or RBAC rule would be written against.

Not reachable from an HTTP request today: tenant identity comes only from the credential map (TENANT_KEY_MAP), and the resolver never takes tenant values from the request body or headers. So this is defence-in-depth. It matters if tenant identity ever comes from a less trusted source (a JWT claim, a database row, a self-service onboarding flow) or if the key map is populated with a malformed tenant id.

Suggested fix

Apply the same path-safety assertions to tenantId and environment in BlobTemplateStore.load (or share the publisher's helpers), failing with a stable PostKitErrorCode before any storage access.

Current behaviour is documented, not fixed

apps/api/src/templates/blob-template-store.security.spec.ts contains a describe block titled "tenant identity is interpolated into the blob name verbatim" that asserts today's behaviour and is clearly marked as a known gap. Those tests must be inverted when this is fixed.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent-readyMeets every criterion in docs/github-source-of-truth.md, section 4 — safe for an agent to claimenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions