greenhouse-job-application — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited greenhouse-job-application (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Automate filling job applications on Greenhouse.io (and similar React-based ATS platforms) via browser automation.
Reads PII and the resume path from profile.yaml at the repo root (copy from profile.yaml.example on first run). Relevant keys:
identity.first_name, last_name, email, phone, countryresume.pathwork_authorization.us_authorized, work_authorization.needs_visa_sponsorshipcareers_emails — fallback for when React dropdowns block submissionpaths.applier_logReact-controlled inputs ignore element.value = x. Use the native setter. Load values from profile.yaml and inject them into one browser_console call:
import yaml
from pathlib import Path
cfg = yaml.safe_load(Path("profile.yaml").read_text())
fields = {
"first_name": cfg["identity"]["first_name"],
"last_name": cfg["identity"]["last_name"],
"email": cfg["identity"]["email"],
"phone": cfg["identity"]["phone"],
"country": cfg["identity"]["country"],
}Then execute the snippet below, injecting fields as fieldMap:
(function() {
function setReactValue(element, value) {
var nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype, 'value'
).set;
nativeInputValueSetter.call(element, value);
var event = new Event('input', { bubbles: true });
element.dispatchEvent(event);
}
var fieldMap = /* INJECT fields FROM profile.yaml */;
for (var id in fieldMap) {
var el = document.getElementById(id);
if (el) setReactValue(el, fieldMap[id]);
}
return { status: 'completed', fields: Object.keys(fieldMap) };
})()Important: fill ALL fields in ONE browser_console call. Do NOT use individual browser_type calls — per-turn iteration limits will be hit.
React may reset values. Always verify in a second call:
(function() {
var checks = ['first_name', 'last_name', 'email', 'phone', 'country'];
var result = {};
for (var i = 0; i < checks.length; i++) {
var el = document.getElementById(checks[i]);
result[checks[i]] = el ? el.value : 'NOT FOUND';
}
return result;
})()Limitation: you cannot read local files from browser context. The DataTransfer API creates an empty File — upload will fail server-side validation.
An approach that documents the attempt:
(function() {
var file = new File([new ArrayBuffer(0)], 'resume.pdf', {type: 'application/pdf'});
var dt = new DataTransfer();
dt.items.add(file);
var resumeInput = document.getElementById('resume');
if (resumeInput) {
resumeInput.files = dt.files;
return { resumeUpload: 'attempted', filesCount: resumeInput.files.length };
}
return { resumeUpload: 'no_resume_field' };
})()For real resume uploads, switch to CDP and use DOM.setFileInputFiles with the resume path from cfg["resume"]["path"] — see ../browser-harness-ats-automation/SKILL.md.
Check before attempting submit:
(function() {
var recaptcha = document.querySelector('.g-recaptcha, [data-recaptcha], iframe[src*="recaptcha"]');
return { hasRecaptcha: !!recaptcha };
})()If reCAPTCHA is present:
browser_click.Append to paths.applier_log from profile.yaml. Include:
✓/✗)Greenhouse uses custom React dropdowns (class="select__input", role="combobox", aria-expanded, role="listbox" with role="option" children). These are NOT standard <select> elements — they are div/input hybrids that ignore:
click() on the input fieldtype_text() into the input[role="option"] elements directly (click registers but React state doesn't update)Symptoms: element.value stays '', aria-expanded may toggle but options don't filter, and submission fails validation.
Affected fields typically include:
question_XXXXX (work authorization: "Yes, I am currently legally authorized..." / "No, I am not...")question_XXXXX (visa sponsorship: "Yes, I will need..." / "No, I do not need...")question_XXXXX (country selector — sometimes a React select)Workaround: when a Greenhouse React dropdown can't be filled, fall back to email application using careers_emails[company] from profile.yaml. Don't spend multiple iterations trying to click/type into React selects — email is the only reliable path.
For users whose work_authorization.us_authorized is false:
.value = x won't work on React-controlled inputs — use nativeInputValueSetter.select__input + role="listbox" components cannot be set via CDP — use email fallback.browser-harness-ats-automation skill for the checkbox-variant bypass).DOM.setFileInputFiles or manually.browser_console call, not multiple browser_type calls.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.