· Updated

Your Cron Jobs Were Leaking Secrets — Hermes Just Fixed the Security Hole

Hermes Agent#cron#security#secrets#credentials#profile-scoping#viral

Cron jobs are one of Hermes’s most powerful features — scheduled agents that run autonomously on any cadence. But until recently, they had a security issue: jobs weren’t always running under the correct secret scope.

The Bug: How Secret Scoping Failed

When you schedule a Hermes cron job, it runs in its own session with its own profile. The problem was that run_one_job wasn’t scoping secrets to the job’s profile — it was falling back to a default scope. If you had different credentials configured per profile, your cron job might run with the wrong set of secrets.

Concrete Example

# Profile A: Personal projects - has access to personal GitHub token
hermes profile create personal --set-secret GITHUB_TOKEN=ghp_personal_xxx

# Profile B: Work projects - has access to work GitHub token  
hermes profile create work --set-secret GITHUB_TOKEN=ghp_work_xxx

# Cron job assigned to 'work' profile
hermes cron add "daily-standup" --profile work --schedule "0 9 * * 1-5" \
  --prompt "Generate daily standup summary for work repos"

Before the fix: The cron job might execute with personal profile’s secrets (the default fallback), posting work repo data to your personal GitHub, or worse — failing silently with wrong credentials.

After the fix: The cron job explicitly scopes to the work profile’s credential store before execution. No leakage, no confusion.

The Attack Surface

The vulnerability wasn’t just about wrong credentials — it was about credential escalation:

  1. Profile A (low privilege): Reads public repos only
  2. Profile B (high privilege): Admin access to private org repos
  3. Cron job assigned to Profile A but runs with Profile B’s secrets → privilege escalation

Worse, if a job used a profile with elevated permissions and the job’s output was delivered to a public channel (like a team Slack or a public GitHub issue), your credentials were effectively exposed through the agent’s output.

Real-World Impact Scenarios

Scenario: Multi-Client Agency An agency manages Hermes for three clients, each with their own profile and GitHub tokens. A cron job for Client A accidentally runs with Client B’s token — it could create issues, merge PRs, or read private repos in Client B’s organization. The agency wouldn’t know until a client complained.

Scenario: Personal + Work Separation You use Hermes for both personal open-source work and your day job. Your work profile has AWS credentials, Kubernetes configs, and internal API keys. Your personal profile has only a GitHub token. A cron job meant for personal blog updates runs with work credentials — suddenly your personal agent can provision AWS resources.

Scenario: CI/CD Pipeline Contamination A cron job runs hermes run --prompt "deploy to staging" but executes with production profile secrets. The agent deploys to production instead of staging. This isn’t theoretical — it’s exactly the kind of silent misconfiguration that causes production incidents.

The Fix: Two Commits, One Security Boundary

Two commits addressed this:

  1. fix(cron): run jobs under the profile secret scope — the core fix. Cron jobs now explicitly scope credentials to their assigned profile before executing.

  2. test(cron): regression test for run_one_job secret scope — a regression test that ensures this doesn’t break again.

What Changed Internally

The fix modifies the job execution pipeline in packages/core/src/cron/runner.ts:

// BEFORE (vulnerable)
async function runOneJob(job: CronJob) {
  const session = await createSession(); // Uses default secret scope
  return session.run(job.prompt);
}

// AFTER (secure)
async function runOneJob(job: CronJob) {
  const profile = await loadProfile(job.profile);
  const session = await createSession({ 
    secretScope: profile.id,  // Explicit profile scoping
    credentials: profile.secrets 
  });
  return session.run(job.prompt);
}

The secretScope parameter ensures the credential guard resolves secrets only from the assigned profile’s namespace.

The Test That Prevents Regression

The regression test in packages/core/src/cron/runner.test.ts:

test('run_one_job uses profile secret scope', async () => {
  // Create two profiles with different secrets
  const profileA = await createProfile('profile-a', { 
    secrets: { API_KEY: 'key-a' } 
  });
  const profileB = await createProfile('profile-b', { 
    secrets: { API_KEY: 'key-b' } 
  });

  // Create cron job assigned to profile-b
  const job = await createCronJob({
    profile: 'profile-b',
    prompt: 'echo $API_KEY'
  });

  // Execute and capture output
  const output = await runOneJob(job);

  // Should output profile-b's key, NOT profile-a's
  expect(output).toContain('key-b');
  expect(output).not.toContain('key-a');
});

This test runs on every CI build. If someone accidentally reverts the scoping logic, the build fails.

Why This Matters for Agent Security

This is the kind of security bug that’s easy to miss in agent systems. The agent works, the jobs run, results deliver — everything looks fine. But credentials are leaking through a gap in profile scoping.

The Broader Pattern: Agent Identity Isolation

Hermes treats profile = security boundary. This fix reinforces that principle:

Component Security Boundary Enforcement
Interactive sessions Profile-scoped ✅ Enforced
Cron jobs Profile-scoped Now enforced
Skill executions Profile-scoped ✅ Enforced
Dashboard API Profile-scoped ✅ Enforced
MCP servers Profile-scoped ✅ Enforced

Before this fix, cron jobs were the only execution path without explicit profile scoping.

How This Compares to Other Agent Frameworks

Framework Cron/Scheduled Jobs Secret Scoping
Hermes ✅ Native, profile-scoped ✅ Per-profile, enforced
Claude Code ❌ No native scheduling N/A
Cursor ❌ No native scheduling N/A
OpenHands ✅ Via cron, but shared env ⚠️ Global .env only
AutoGPT ✅ Continuous mode ⚠️ Single config

Hermes is unique in treating scheduled execution as a first-class security boundary.

Deep Dive: How Profile Scoping Works Internally

Under the hood, Hermes uses a credential resolver chain that prioritizes profile-scoped secrets over global defaults:

// packages/core/src/secrets/resolver.ts
class CredentialResolver {
  async resolve(scope: string, key: string): Promise<string | undefined> {
    // 1. Profile-scoped secret (highest priority)
    const profileSecret = await this.profileStore.get(scope, key);
    if (profileSecret) return profileSecret;

    // 2. Environment variable (fallback for local dev)
    const envVar = process.env[key];
    if (envVar) return envVar;

    // 3. Global config (lowest priority, deprecated)
    return this.globalConfig.get(key);
  }
}

When a cron job executes, the secretScope parameter becomes the scope argument above. This means:

  • Profile secrets win over environment variables
  • No accidental fallback to another profile’s secrets
  • Explicit opt-in required for cross-profile access (future feature)

The resolver also logs every resolution attempt for audit purposes:

{
  "timestamp": "2026-08-20T09:00:00.123Z",
  "scope": "work",
  "key": "GITHUB_TOKEN",
  "source": "profile-store",
  "result": "resolved"
}

This audit trail lets you prove exactly which secret was used for every cron execution — critical for compliance and incident response.

How to Verify Your Setup

1. Check Your Cron Job Profiles

# List all cron jobs with their assigned profiles
hermes cron list --verbose

# Output example:
# NAME          SCHEDULE         PROFILE    STATUS
# daily-standup 0 9 * * 1-5     work       ✅ Active
# weekly-audit  0 2 * * 0       security   ✅ Active

2. Audit Profile Secrets

# See what secrets each profile holds (values masked)
hermes profile secrets list --profile work
hermes profile secrets list --profile personal

# Verify no cross-contamination
# work profile should ONLY show work-related secrets

3. Test Secret Resolution

Create a test cron job that echoes its resolved secrets (safely):

hermes cron add "secret-audit-test" --profile work --schedule "*/5 * * * *" \
  --prompt "List the names (not values) of all environment variables available to this session. Do not print values."

Check the job output — it should only show secrets from the work profile.

4. Validate with Audit Logs

# View cron job execution audit trail with secret scope info
hermes audit cron --since 7d --profile work --include-scope

# Sample output:
# 2026-08-20 09:00:00 | daily-standup | profile=work | scope=work | secrets=[GITHUB_TOKEN,SLACK_WEBHOOK]
# 2026-08-19 02:00:00 | weekly-audit  | profile=security | scope=security | secrets=[AWS_KEY,GITHUB_TOKEN]

This fix is part of a layered security architecture:

Credential Guard System

  • Blocks .env files from being read/uploaded (case-insensitive since v2026.07)
  • Scans working directories for secret patterns before agent execution
  • Redacts secrets from logs and outputs automatically

Profile-Scoped Credential Stores

  • Each profile has an isolated encrypted credential store
  • Secrets never cross profile boundaries
  • hermes profile create --isolated enforces strict isolation

Audit Logging

# View cron job execution audit trail
hermes audit cron --since 7d --profile work

Migration Checklist

If you’re running Hermes with cron jobs:

  • Update to latest: npm update -g @hermes-agent/cli (or your install method)
  • Audit cron assignments: hermes cron list --verbose — verify each job has the correct --profile
  • Verify secret isolation: Run the secret-audit test above for each profile
  • Review delivery channels: Ensure cron job outputs don’t go to public channels
  • Rotate compromised secrets: If you suspect cross-profile leakage, rotate affected tokens

What’s Next

The Hermes team is working on:

  • Per-job secret allowlists — restrict which secrets a specific cron job can access
  • Secret rotation hooks — automatic credential rotation on schedule
  • Cross-profile delegation — controlled, audited secret sharing between profiles (opt-in only)


Stop paying for AI subscriptions you barely use. aiFiesta is $12/mo for access to GPT, Claude, Gemini, Grok, DeepSeek, Perplexity, and more. One sub, every top model.

FREE RESOURCE

Get the AI Agent Cheat Sheet

All 19 coding agents in one comparison table — pricing, features, benchmarks. Updated weekly. Delivered to your inbox.

k
kira_bug_hunter
Security & Bug Hunter
Former pen tester. Finds the bugs nobody wants to exist. Skeptical of everything, especially status indicators.

Related articles