Skip to content

[Bug]: Cron Store Silently Returns Empty Jobs on Any Read Error #10208

@coygeek

Description

@coygeek

Summary

The loadCronStore function in src/cron/store.ts catches all errors and returns an empty job list. When the cron jobs file is corrupted, has permission issues, or contains malformed JSON5, all scheduled jobs silently disappear. The subsequent save operation overwrites the (potentially recoverable) file with empty data.

Affected Code

File: src/cron/store.ts:22-34

export async function loadCronStore(storePath: string): Promise<CronStoreFile> {
  try {
    const raw = await fs.promises.readFile(storePath, "utf-8");
    const parsed = JSON5.parse(raw);
    const jobs = Array.isArray(parsed?.jobs) ? (parsed?.jobs as never[]) : [];
    return {
      version: 1,
      jobs: jobs.filter(Boolean) as never as CronStoreFile["jobs"],
    };
  } catch {
    return { version: 1, jobs: [] };  // Silent data loss
  }
}

The bare catch treats every error (EACCES, EIO, malformed JSON5) the same as ENOENT (file not found), returning an empty store. Callers like ensureLoaded() then persist the empty store, overwriting the original file.

Steps to Reproduce

  1. Configure several cron jobs for automated operations
  2. Corrupt the cron store file (inject invalid JSON5, change permissions, or simulate disk error)
  3. Restart gateway or trigger config reload
  4. loadCronStore catches error, returns empty jobs
  5. All scheduled automation silently stops
  6. No warning in logs, no indication to operator

Expected Behavior

  • ENOENT (file not found) should return an empty store (this is normal for first run)
  • All other errors (EACCES, EIO, corrupted JSON) should throw so the caller can handle the failure explicitly
  • The corrupted file should be preserved for recovery

CVSS Assessment

Metric Value
Score 7.7 / 10.0
Severity High
Vector CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H

References

  • CWE: CWE-755 - Improper Handling of Exceptional Conditions

Attack Surface

How is this reached?

  • Network (HTTP/WebSocket endpoint, API call)
  • Adjacent Network (same LAN, requires network proximity)
  • Local (local file, CLI argument, environment variable)
  • Physical (requires physical access to machine)

Authentication required?

  • None (unauthenticated/public access)
  • Low (any authenticated user)
  • High (admin/privileged user only)

Entry point: Cron store file read on startup or reload

Exploit Conditions

Complexity:

  • Low (no special conditions, works reliably)
  • High (requires race condition, specific config, or timing)

User interaction:

  • None (automatic, no victim action needed)
  • Required (victim must click, visit, or perform action)

Prerequisites: File corruption, permission change, or disk error

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions