Skip to content

Safety/security fixes for auto-apply (dry-run side effects, agent sandboxing, screening answers, file perms)#59

Open
sebastianmukuria wants to merge 6 commits into
Pickle-Pixel:mainfrom
sebastianmukuria:fix/apply-safety
Open

Safety/security fixes for auto-apply (dry-run side effects, agent sandboxing, screening answers, file perms)#59
sebastianmukuria wants to merge 6 commits into
Pickle-Pixel:mainfrom
sebastianmukuria:fix/apply-safety

Conversation

@sebastianmukuria

Copy link
Copy Markdown

Summary

Safety and security fixes for the auto-apply path, found during a pre-flight review of the codebase. This is 1 of 4 focused PRs from that review (safety, discovery/scoring, tailoring/apply output, setup) — each is independent and can be reviewed/merged on its own.

What & why

  • apply --dry-run is now genuinely side-effect-free. Previously a dry run still told the agent to send_email on the email-only path and to emit RESULT:APPLIED, which the launcher wrote to the DB as apply_status='applied' — permanently removing the job from the real queue. Dry run now emits a dedicated RESULT:DRYRUN, sends no mail (the Gmail send tool is also disallowed in dry-run), and the worker releases the lock without marking applied. (apply/prompt.py, apply/launcher.py)
  • The apply agent is no longer effectively unsandboxed. It runs with bypassPermissions; the disallowed-tools list now also blocks Bash/Edit/Write/MultiEdit/NotebookEdit/WebFetch/WebSearch/Task/KillShell (Read + Playwright + the Gmail tools it needs stay), and the prompt refuses instructions embedded in page content (prompt-injection guard). A malicious job page can no longer trivially run shell or exfiltrate ~/.applypilot. (apply/launcher.py, apply/prompt.py)
  • Screening answers come from the profile, not hardcoded. Age/background-check/felony/"previously worked here" were hardcoded for every user; they now read profile.screening, and unset legally significant answers are flagged for the human instead of guessed. The agent no longer claims experience with tools the candidate hasn't listed. (apply/prompt.py, wizard/init.py, profile.example.json)
  • apply --url queue bug. apply_status != 'in_progress' is NULL for fresh jobs in SQLite, so --url always reported "queue empty"; it also didn't exclude already-applied jobs (duplicate applications). Fixed the predicate. (apply/launcher.py)
  • Secret-bearing files are written 0600 (profile.json, .env, prompt logs, MCP configs, the SQLite DB) and ~/.applypilot is 0700. (config.py, database.py, wizard/init.py, apply/launcher.py)

Tests

Adds regression tests: tests/test_dryrun.py, tests/test_prompt_screening.py, tests/test_acquire_job.py, tests/test_permissions.py (15 tests, all passing). CHANGELOG updated under [Unreleased].

sebastianmukuria and others added 6 commits June 9, 2026 21:13
- Dry-run prompt emits RESULT:DRYRUN, never RESULT:APPLIED
- Email-only step does not send mail in dry-run; send_email tool disallowed
- run_job recognizes DRYRUN (matched first); worker loop releases lock and
  falls through instead of marking applied
- Guard against re-selecting the same released job forever (seen_urls)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Disallow Bash/Edit/Write/MultiEdit/NotebookEdit/WebFetch/WebSearch/Task/
  KillShell built-ins (Read and Playwright/Gmail-send stay available)
- Add a prompt-injection guard to the NEVER DO THESE prompt section

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Read age/background-check/felony/how-heard from profile['screening'];
  legally significant answers default to NOT PROVIDED (agent asks the human)
- Drop hardcoded 'Previously Worked Here: No'; check it against resume history
- Replace 'answer YES for same-domain tools' with honest skills-from-profile
- Add screening section to profile.example.json and the init wizard

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SQLite NULL != 'in_progress' is NULL, so fresh jobs never matched the
target-URL query ('queue empty'). Select rows that are NULL or not
in_progress/applied, and exclude already-applied_at to prevent duplicates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add config.write_private_text() (write + chmod 0600); use for profile.json,
  .env (all three write sites), generated prompt logs, and MCP configs
- chmod the SQLite DB to 0600 in init_db; chmod APP_DIR to 0700 in ensure_dirs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant