// ==UserScript== // @name Hire Security Clearance Indicator // @namespace https://hire.amazon.com // @version 2.4.1 // @description Scans hire.amazon.com search results for security clearance and citizenship keywords, injects color-coded badges, and provides filter controls. // @match https://hire.amazon.com/* // @updateURL https://raw.githubusercontent.com/scottkor22/HIRE-Security-Clearance-Indicator/main/hire-security-clearance-indicator.user.js // @downloadURL https://raw.githubusercontent.com/scottkor22/HIRE-Security-Clearance-Indicator/main/hire-security-clearance-indicator.user.js // @grant GM_setValue // @grant GM_getValue // @grant GM_xmlhttpRequest // @connect document-service-data-prod.s3.us-west-2.amazonaws.com // @connect *.amazonaws.com // @require https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js // ==/UserScript== (function () { 'use strict'; // ── PageDetector ────────────────────────────────────────────────────── /** * Determines whether the current page is a hire.amazon.com search results page. * Checks the hostname and pathname against known search results URL patterns. * @returns {boolean} */ function isSearchResultsPage() { var loc = window.location; if (loc.hostname !== 'hire.amazon.com') { return false; } return /\/(search|sourcing\/(pools|discover))(\/|$|\?)/.test(loc.pathname); } // ── KeywordMatcher ───────────────────────────────────────────────── /** * Clearance patterns ordered from most specific to least specific. * All use word boundaries (\b) and case-insensitive flag (i). */ var CLEARANCE_PATTERNS = [ { label: 'TS/SCI with Full Scope Polygraph', regex: /\b(?:active|interim|current)?\s*TS[\/,]\s*SCI\s*(?:with|w\/|\+)\s*(?:current\s+|active\s+)?(?:full\s*scope\s*)?(?:poly(?:graph)?|lifestyle|FSP)\b/i }, { label: 'TS/SCI with Full Scope Polygraph', regex: /\bTop\s+Secret\s+SCI\s+(?:with|w\/)\s+(?:current\s+|active\s+)?(?:full\s+scope\s+)?(?:poly(?:graph)?|FSP)\b/i }, { label: 'TS/SCI with Full Scope Polygraph', regex: /\b(?:active|interim|current)?\s*TS\s+(?:with|w\/|\+)\s+full\s+scope\b/i }, { label: 'TS/SCI with CI Polygraph', regex: /\b(?:active|interim|current)?\s*TS[\/,]\s*SCI\s*(?:with|w\/|\+)\s*(?:current\s+|active\s+)?(?:CI\s+Poly(?:graph)?|counter\s+intelligence\s+poly(?:graph)?)\b/i }, { label: 'TS/SCI with CI Polygraph', regex: /\bTop\s+Secret\s+SCI\s+(?:with|w\/)\s+(?:current\s+|active\s+)?CI\s+Poly(?:graph)?\b/i }, { label: 'TS/SCI with Polygraph', regex: /\b(?:active|interim|current)?\s*TS[\/,]\s*SCI\s*(?:with|w\/|\+)\s*(?:current\s+|active\s+)?(?:Poly(?:graph)?)\b/i }, { label: 'TS/SCI', regex: /\b(?:active|interim|current)?\s*TS[\/,]\s*SCI\b|\bTop\s+Secret\s+SCI\b|\bTop\s+Secret\s+(?:Clearance\s+)?(?:with\s+)?Sensitive\s+Compartmented\s+Information/i }, { label: 'L2 Cleared', regex: /\b(?:L2|Level\s+2)\s+(?:Cleared?|Clearance)\b/i }, { label: 'L1 Cleared', regex: /\b(?:L1|Level\s+1)\s+(?:Cleared?|Clearance)\b/i }, { label: 'Interim TS', regex: /\binterim\s+(?:top\s+secret|TS)\b/i }, { label: 'Top Secret', regex: /\b(?:active|interim|current)?\s*Top\s+Secret\b/i }, { label: 'Yankee White Clearance', regex: /\b(?:active|current)?\s*Yankee\s+White\s+Clearance\b/i }, { label: 'DoD Secret', regex: /\b(?:active|interim|current)?\s*(?:DoD\s+)?Secret\s+(?:security\s+)?clearance\b/i }, { label: 'Public Trust', regex: /\bPublic\s+Trust\b/i }, { label: 'DOE Q', regex: /\b(?:active|current)?\s*DOE\s+Q\b/i }, { label: 'Secret', regex: /(?:Answer\s*:\s*|A\.\s*)(?:active\s+|interim\s+|current\s+|DoD\s+)?Secret\b|(?:active|interim|current|DoD)\s+Secret\b|\bSecret\s+(?:security\s+)?clearance\b/i }, ]; /** * Citizenship patterns ordered from most specific to least specific. */ var CITIZENSHIP_PATTERNS = [ { label: 'US Citizenship', regex: /(?:Answer\s*:|A\.)\s*US\s+Citizenship\b/i }, { label: 'US Citizen', regex: /(?:Answer\s*:|A\.)\s*US\s+Citizen\b/i }, { label: 'United States of America', regex: /(?:Answer\s*:|A\.)\s*United\s+States\s+of\s+America\b/i }, { label: 'US Citizen', regex: /United\s+States\s+Citizenship/i }, ]; /** * Runs all clearance and citizenship patterns against the input text. * Returns a MatchResult with deduplicated clearances, hasCitizenship flag, * and the first matched citizenshipKeyword. * @param {string} text * @returns {{ clearances: string[], hasCitizenship: boolean, citizenshipKeyword: string|null }} */ function match(text) { var clearances = []; var hasCitizenship = false; var citizenshipKeyword = null; if (typeof text !== 'string' || text.length === 0) { return { clearances: clearances, hasCitizenship: hasCitizenship, citizenshipKeyword: citizenshipKeyword }; } // Strip out false-positive phrases before matching // Normalize whitespace (PDF text layers often split words across spans) var cleanText = text .replace(/\s+/g, ' ') // Fix common PDF word splits for clearance terms .replace(/F\s+ull/gi, 'Full') .replace(/S\s+cope/gi, 'Scope') .replace(/P\s+oly/gi, 'Poly') .replace(/C\s+learance/gi, 'Clearance') .replace(/S\s+ecret/gi, 'Secret') .replace(/C\s+ompartmented/gi, 'Compartmented') .replace(/S\s+ensitive/gi, 'Sensitive') .replace(/C\s+itizen/gi, 'Citizen') // Strip "I have an active clearance at X level" questions answered "No" .replace(/I have an active US government security clearance at the[\s\S]{0,100}?level[\s\S]{0,50}?Answer:\s*No/gi, '') .replace(/I have an active US government security clearance at the[\s\S]{0,100}?level[\s\S]{0,50}?A\.\s*No/gi, '') // Remove our own badge text concatenated with page chrome .replace(/(?:TS\/SCI \+ L2|TS\/SCI \+ L1|TS\/SCI \+ Poly|TS\/SCI|Interim TS|Top Secret|DoD Secret|Public Trust|Yankee White|DOE Q|L1|L2|Secret|US Citizen)(?=\w|Open|Page|ID|Hide|Show|×)/g, ' ') // Strip screening question body text (questions about willingness/eligibility to obtain clearance) .replace(/(?:would you be |are you )(?:eligible|able|willing)[^?]{0,200}\?/gi, '') .replace(/(?:do you (?:currently )?(?:hold|have|possess))[^?]{0,200}\?/gi, '') // Diamond/DOD/Topaz eligibility notes .replace(/(?:Diamond|DOD|Topaz):\s*Candidate\s+appears\s+eligible\s+for\s+[^.]{0,100}/gi, '') .replace(/eligible\s+for\s+Level\s+(?:I|II|1|2)\s+initial\s+only/gi, '') .replace(/eligible\s+for\s+(?:initial\s+only|Dual\s+Track\s+processing\s+type)/gi, '') // Strip known false-positive phrases .replace(/Security\s+Clearance\s*>\s*Security\s+Clearance\s+\w+/gi, '') .replace(/background\s+investigation\s+and\s+polygraph\s+examination/gi, '') .replace(/Security Check/g, '') .replace(/Rehire [Ee]ligibility[^.]{0,100}/g, '') .replace(/Profile Exclusion Security Check[^.]{0,100}/gi, ''); // Track which labels we've already added to avoid duplicates var seen = {}; for (var i = 0; i < CLEARANCE_PATTERNS.length; i++) { var pattern = CLEARANCE_PATTERNS[i]; if (pattern.regex.test(cleanText) && !seen[pattern.label]) { // For compound patterns, also mark their sub-patterns as seen // e.g. "TS/SCI with Polygraph" should suppress standalone "TS/SCI" and "Polygraph" if (pattern.label === 'TS/SCI with Full Scope Polygraph') { seen['TS/SCI with CI Polygraph'] = true; seen['TS/SCI with Polygraph'] = true; seen['TS/SCI'] = true; seen['Full Scope Polygraph'] = true; seen['CI Polygraph'] = true; seen['Polygraph'] = true; seen['ISSA'] = true; seen['ISA'] = true; seen['Clearance'] = true; seen['Secret'] = true; seen['Top Secret'] = true; seen['Interim TS'] = true; seen['DoD Secret'] = true; seen['L2 Cleared'] = true; seen['L1 Cleared'] = true; } if (pattern.label === 'TS/SCI with CI Polygraph') { seen['TS/SCI with Polygraph'] = true; seen['TS/SCI'] = true; seen['CI Polygraph'] = true; seen['Polygraph'] = true; seen['ISSA'] = true; seen['ISA'] = true; seen['Secret'] = true; seen['Top Secret'] = true; seen['Interim TS'] = true; seen['L1 Cleared'] = true; } if (pattern.label === 'TS/SCI with Polygraph') { seen['TS/SCI'] = true; seen['Polygraph'] = true; seen['ISSA'] = true; seen['ISA'] = true; seen['Secret'] = true; seen['Top Secret'] = true; seen['Interim TS'] = true; } if (pattern.label === 'TS/SCI') { seen['Secret'] = true; seen['Top Secret'] = true; seen['Interim TS'] = true; seen['DoD Secret'] = true; seen['L2 Cleared'] = true; seen['L1 Cleared'] = true; seen['Full Scope Polygraph'] = true; seen['CI Polygraph'] = true; } if (pattern.label === 'L2 Cleared') { seen['L1 Cleared'] = true; seen['Full Scope Polygraph'] = true; seen['CI Polygraph'] = true; seen['Polygraph'] = true; seen['ISSA'] = true; seen['ISA'] = true; seen['Clearance'] = true; } if (pattern.label === 'L1 Cleared') { seen['CI Polygraph'] = true; seen['Polygraph'] = true; seen['ISA'] = true; seen['Clearance'] = true; } if (pattern.label === 'Full Scope Polygraph') { seen['CI Polygraph'] = true; seen['L1 Cleared'] = true; seen['Polygraph'] = true; seen['ISSA'] = true; seen['ISA'] = true; seen['Clearance'] = true; } if (pattern.label === 'CI Polygraph') { seen['Polygraph'] = true; seen['ISA'] = true; seen['Clearance'] = true; } if (pattern.label === 'Interim TS') { seen['Top Secret'] = true; seen['Secret'] = true; } if (pattern.label === 'Top Secret') { seen['Secret'] = true; } if (pattern.label === 'DoD Secret') { seen['Secret'] = true; seen['Clearance'] = true; } if (pattern.label === 'Public Trust') { seen['Clearance'] = true; } if (pattern.label === 'Yankee White Clearance') { seen['Clearance'] = true; } seen[pattern.label] = true; clearances.push(pattern.label); } } for (var j = 0; j < CITIZENSHIP_PATTERNS.length; j++) { var cp = CITIZENSHIP_PATTERNS[j]; if (cp.regex.test(cleanText)) { hasCitizenship = true; if (!citizenshipKeyword) { citizenshipKeyword = cp.label; } if (cp.label === 'US Citizenship') { break; } } } // Suppress US Citizen badge if "Permanent resident" appears as an answer if (hasCitizenship && /(?:Answer\s*:\s*|A\.\s*)Permanent\s+resident/i.test(cleanText)) { hasCitizenship = false; citizenshipKeyword = null; } return { clearances: clearances, hasCitizenship: hasCitizenship, citizenshipKeyword: citizenshipKeyword }; } // ── DataExtractor ─────────────────────────────────────────────────── /** * Configurable CSS selectors for hire.amazon.com DOM elements. * Update these if the site's DOM structure changes. */ var DATA_EXTRACTOR_SELECTORS = { candidateCard: '[data-test-id="resultCards-parent"]', candidateName: '[data-test-id="resultCard-name"]', resumeSection: '[data-test-id="job-experience-col"]', notesSection: '[data-test-id="visible-skills"]', questionsSection: '[data-test-id="education-text"]', searchResultsWrapper: '[data-test-id="search-results-wrapper"]', searchCardHeader: '[data-test-id="search-card-header"]', }; /** * Extracts text content from a single section within a candidate card. * Logs an error and returns empty string if the section is missing. * @param {HTMLElement} cardElement * @param {string} selector - CSS selector for the section * @param {string} candidateName - Name of the candidate (for error logging) * @param {string} sourceName - Human-readable name of the source (for error logging) * @returns {string} */ function extractSection(cardElement, selector, candidateName, sourceName) { var section = cardElement.querySelector(selector); if (!section) { // Silently return empty — these sections may not exist on every card return ''; } return section.textContent || ''; } /** * Extracts text from all profile data sources for a single candidate card element. * Returns a CandidateData object, or null if the card has no identifiable name element. * @param {HTMLElement} cardElement * @returns {{ cardElement: HTMLElement, candidateName: string, resumeText: string, notesText: string, questionsText: string } | null} */ function extractOne(cardElement) { var nameEl = cardElement.querySelector(DATA_EXTRACTOR_SELECTORS.candidateName); var candidateName = nameEl ? (nameEl.textContent || '').trim() : 'Unknown'; // Extract text from specific sections, falling back gracefully var resumeText = extractSection( cardElement, DATA_EXTRACTOR_SELECTORS.resumeSection, candidateName, 'job experience' ); var notesText = extractSection( cardElement, DATA_EXTRACTOR_SELECTORS.notesSection, candidateName, 'skills' ); var questionsText = extractSection( cardElement, DATA_EXTRACTOR_SELECTORS.questionsSection, candidateName, 'education' ); // Also grab the full card text as a fallback to catch any clearance/citizenship // keywords that might appear in chips, labels, or other sections var fullCardText = cardElement.textContent || ''; return { cardElement: cardElement, candidateName: candidateName, resumeText: resumeText, notesText: notesText, questionsText: questionsText, fullCardText: fullCardText, }; } /** * Extracts text from all profile data sources for all candidate cards on the page. * @returns {Array<{ cardElement: HTMLElement, candidateName: string, resumeText: string, notesText: string, questionsText: string }>} */ function extractAll() { var cards = document.querySelectorAll(DATA_EXTRACTOR_SELECTORS.candidateCard); var results = []; for (var i = 0; i < cards.length; i++) { var data = extractOne(cards[i]); if (data) { results.push(data); } } return results; } // ── BadgeRenderer ───────────────────────────────────────────────────── /** * Post-processing dedup: removes redundant lower-level badges from a clearances array. * This ensures cached results from before dedup fixes are also cleaned up. */ function dedupClearances(clearances) { var dominated = {}; var dominated_by = { 'TS/SCI with Full Scope Polygraph': ['TS/SCI with CI Polygraph','TS/SCI with Polygraph','TS/SCI','Full Scope Polygraph','CI Polygraph','Polygraph','ISSA','ISA','Clearance','Secret','Top Secret','Interim TS','DoD Secret','L2 Cleared','L1 Cleared'], 'TS/SCI with CI Polygraph': ['TS/SCI with Polygraph','TS/SCI','CI Polygraph','Polygraph','ISA','Clearance','Secret','Top Secret','Interim TS','L1 Cleared'], 'TS/SCI with Polygraph': ['TS/SCI','Polygraph','ISA','ISSA','Clearance','Secret','Top Secret','Interim TS'], 'TS/SCI': ['Secret','Top Secret','Interim TS','DoD Secret','L2 Cleared','L1 Cleared','Full Scope Polygraph','CI Polygraph'], 'L2 Cleared': ['L1 Cleared','Full Scope Polygraph','CI Polygraph','Polygraph','ISSA','ISA','Clearance'], 'L1 Cleared': ['CI Polygraph','Polygraph','ISA','Clearance'], 'Full Scope Polygraph': ['CI Polygraph','Polygraph','ISSA','ISA','Clearance','L1 Cleared'], 'CI Polygraph': ['Polygraph','ISA','Clearance'], 'Top Secret': ['Secret'], 'Interim TS': ['Top Secret','Secret'], 'DoD Secret': ['Secret','Clearance'], 'Public Trust': ['Clearance'], 'Yankee White Clearance': ['Clearance'], }; for (var i = 0; i < clearances.length; i++) { var subs = dominated_by[clearances[i]]; if (subs) { for (var j = 0; j < subs.length; j++) { dominated[subs[j]] = true; } } } return clearances.filter(function (c) { return !dominated[c]; }); } /** * Badge configuration for clearance keywords. * Maps clearance label to display text and CSS color class. */ var CLEARANCE_BADGE_CONFIG = { 'TS/SCI with Full Scope Polygraph': { text: 'TS/SCI + L2', colorClass: 'hsc-badge-fsp' }, 'TS/SCI with CI Polygraph': { text: 'TS/SCI + L1', colorClass: 'hsc-badge-ci-poly' }, 'TS/SCI with Polygraph': { text: 'TS/SCI + Poly', colorClass: 'hsc-badge-tssci-poly' }, 'TS/SCI': { text: 'TS/SCI', colorClass: 'hsc-badge-tssci' }, 'L2 Cleared': { text: 'L2', colorClass: 'hsc-badge-fsp' }, 'L1 Cleared': { text: 'L1', colorClass: 'hsc-badge-ci-poly' }, 'Interim TS': { text: 'Interim TS', colorClass: 'hsc-badge-top-secret' }, 'Top Secret': { text: 'Top Secret', colorClass: 'hsc-badge-top-secret' }, 'DoD Secret': { text: 'DoD Secret', colorClass: 'hsc-badge-secret' }, 'Public Trust': { text: 'Public Trust', colorClass: 'hsc-badge-clearance' }, 'Secret': { text: 'Secret', colorClass: 'hsc-badge-secret' }, 'DOE Q': { text: 'DOE Q', colorClass: 'hsc-badge-doe-q' }, 'Yankee White Clearance':{ text: 'Yankee White', colorClass: 'hsc-badge-clearance' }, }; /** * Badge configuration for citizenship keywords. */ var CITIZENSHIP_BADGE_CONFIG = { text: 'US Citizen', colorClass: 'hsc-badge-citizenship', }; /** * Injects badge CSS styles into document.head. * Appends a