All Docs
FeaturesMaking Tax DigitalUpdated February 19, 2026

Security Advisory: Batch Job Privilege Escalation (v1.0.5)

Security Advisory: Batch Job Privilege Escalation

Advisory ID: SA-2025-001
Fixed in: v1.0.5
Severity: Critical
Affected versions: All versions prior to v1.0.5
Affected file: src/platform/batches/router.ts


Summary

A privilege escalation vulnerability was identified in the batch job router. All five batch procedures — batches.list, batches.runs, batches.trigger, batches.setStatus, and batches.sync — were protected only by protectedProcedure, which checks authentication but does not enforce organisation membership, role requirements, or tenant isolation.

Any authenticated user could exploit this to interact with batch jobs belonging to any organisation in the system, including triggering destructive system-level cron jobs.


Vulnerability Details

Root Cause

The batch router used protectedProcedure as the sole authorization gate across all endpoints. This procedure confirms that a valid session exists, but it does not:

  • Verify the user belongs to the organisation that owns the batch job
  • Check whether the user holds an admin or owner role
  • Distinguish between tenant-scoped and system-level operations

Impact

An attacker with any valid user account could:

  • Enumerate batch jobs across all tenants via batches.list
  • Inspect run histories for any organisation via batches.runs
  • Trigger any batch job via batches.trigger, including:
    • The audit-log cleanup cron (resulting in deletion of all audit logs across the platform)
    • The subscription-sync job (disrupting billing state for other tenants)
  • Pause, resume, or cancel any batch job via batches.setStatus
  • Force a sync of any batch job via batches.sync

This constitutes both a data integrity risk (batch jobs can be triggered or cancelled outside of their normal schedule) and a data loss risk (destructive cron jobs can be executed by unauthorised users).

Affected Procedures

batches.list
batches.runs
batches.trigger
batches.setStatus
batches.sync

All located in: src/platform/batches/router.ts


Remediation

v1.0.5 upgrades the authorization guards on all affected procedures:

  • batches.list, batches.runs, batches.trigger, batches.setStatus are now gated by adminProcedure or ownerProcedure, which enforce that the requesting user holds the appropriate role within the correct organisation.
  • batches.sync and other system-level job procedures are now gated by a new systemAdminProcedure, which additionally checks a superadmin flag on the user record. This ensures platform-wide operations cannot be invoked by any regular or org-level admin.

Before (vulnerable)

// src/platform/batches/router.ts
export const batchesRouter = router({
  list: protectedProcedure.query(...),
  runs: protectedProcedure.query(...),
  trigger: protectedProcedure.mutation(...),
  setStatus: protectedProcedure.mutation(...),
  sync: protectedProcedure.mutation(...),
});

After (fixed)

// src/platform/batches/router.ts
export const batchesRouter = router({
  list: adminProcedure.query(...),
  runs: adminProcedure.query(...),
  trigger: adminProcedure.mutation(...),
  setStatus: ownerProcedure.mutation(...),
  sync: systemAdminProcedure.mutation(...),
});

Recommended Actions

For all deployments

  1. Upgrade to v1.0.5 immediately.
  2. Review your audit logs for unexpected calls to the affected procedures from non-admin users. Focus on:
    • batches.trigger — look for executions outside of normal scheduled windows
    • batches.setStatus — look for unexpected pause, resume, or cancel events
    • batches.sync — look for manual sync invocations from non-system accounts
  3. Verify audit log integrity. If the audit-cleanup cron was triggered by an unauthorised user, audit log data may have been deleted. Cross-reference with any external log aggregation (e.g. your HMRC MTD submission records) to detect gaps.
  4. Check subscription state. If batches.sync was invoked unexpectedly, review your AgentOS and HMRC MTD subscription sync records for any discrepancies.

For self-hosted deployments

  • Ensure your users table includes a superadmin boolean column, as the new systemAdminProcedure reads this field. The v1.0.5 migration handles this automatically for managed deployments.
  • Review any custom role assignments to ensure no regular members have been incorrectly granted admin or owner roles that would now give them batch job access.

Timeline

DateEvent
IdentifiedVulnerability discovered during internal security review
FixedPatch implemented in src/platform/batches/router.ts
Releasedv1.0.5 published

References