// ==UserScript==
// @tampermonkey-safari-promotion-code-request c9263fb7-83eb-4bf0-9703-778d05b1b33e
// @license MIT
// @name IRCC Security Checker
// @namespace http://tampermonkey.net/
// @version 1.7.0
// @description Check IRCC application security status from tracker data, show all history with explanations
// @author WeChat ID: RoaldAmundsen (https://security-checker.raindrop.eu.org for updates)
// @match https://ircc-tracker-suivi.apps.cic.gc.ca/en/login*
// @match https://ircc-tracker-suivi.apps.cic.gc.ca/en/overview*
// @match https://ircc-tracker-suivi.apps.cic.gc.ca/en/applications/details*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @run-at document-start
// @inject-into auto
// ==/UserScript==
(function() {
'use strict';
// Key meanings based on information collected from web research
const KEY_CODE_MEANINGS = {
'security': { meaning: 'Background/Security process', sign: 'unknown' },
'medical': { meaning: 'Medical-related item', sign: 'neutral' },
'imm1017': { meaning: 'Medical Instructions (IMM 1017)', sign: 'neutral' },
'word ltr 02': { meaning: 'Letter generated/uploaded', sign: 'neutral' },
'initial': { meaning: 'Tracker initialized', sign: 'neutral' },
'57': { meaning: 'Internal IRCC code', sign: 'unknown' },
'93': { meaning: 'Internal IRCC code', sign: 'unknown' },
'871': { meaning: 'Internal IRCC code', sign: 'unknown' },
'86': { meaning: 'Internal IRCC code', sign: 'unknown' },
'626': { meaning: 'Internal IRCC code', sign: 'unknown' },
'648': { meaning: 'Internal IRCC code', sign: 'unknown' },
'953': { meaning: 'Internal IRCC code', sign: 'unknown' },
'408': { meaning: 'Internal IRCC code', sign: 'unknown' },
'67': { meaning: 'Internal IRCC code', sign: 'unknown' },
'9': { meaning: 'Internal IRCC code', sign: 'unknown' },
'41': { meaning: 'Internal IRCC code', sign: 'unknown' },
'622': { meaning: 'Internal IRCC code', sign: 'unknown' },
'633': { meaning: 'Internal IRCC code', sign: 'unknown' },
'42': { meaning: 'Internal IRCC code', sign: 'unknown' },
'729': { meaning: 'Internal IRCC code', sign: 'unknown' },
'8': { meaning: 'Internal IRCC code', sign: 'unknown' },
'637': { meaning: 'Internal IRCC code', sign: 'unknown' }
};
const SIGN_COLOR = {
positive: '#28a745',
neutral: '#6c757d',
unknown: '#0d6efd',
negative: '#dc3545'
};
// Capture tracker JSON from network
(function captureFromNetwork(){
if (window.__IRCC_NET_CAPTURED__) return;
window.__IRCC_NET_CAPTURED__ = true;
const saveIfTracker = (obj) => {
if (hasHistoryArray(obj)) {
window.__IRCC_TRACKER_DATA = obj;
window.dispatchEvent(new CustomEvent('ircc-tracker-data', { detail: { source: 'network' }}));
}
};
const _fetch = window.fetch;
window.fetch = async function(...args){
const resp = await _fetch.apply(this, args);
try {
const clone = resp.clone();
const ct = clone.headers.get('content-type') || '';
if (ct.includes('application/json')) {
const json = await clone.json();
saveIfTracker(json);
}
} catch {}
return resp;
};
const _open = XMLHttpRequest.prototype.open;
const _send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(...a){ this.__ircc_url = a[1]; return _open.apply(this,a); };
XMLHttpRequest.prototype.send = function(...a){
this.addEventListener('load', function(){
try {
const ct = this.getResponseHeader && this.getResponseHeader('content-type') || '';
if (ct.includes('application/json')) {
const json = JSON.parse(this.responseText);
saveIfTracker(json);
}
} catch {}
});
return _send.apply(this,a);
};
})();
function createUI() {
if (document.getElementById('ircc-security-checker')) return;
const wrap = document.createElement('div');
wrap.id = 'ircc-security-checker';
wrap.innerHTML = `
<div style="
position: fixed;
top: 20px;
right: 20px;
z-index: 100000;
background: #fff;
border: 2px solid #007bff;
border-radius: 8px;
padding: 12px;
width: 340px;
box-shadow: 0 6px 18px rgba(0,0,0,0.2);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, 'Noto Sans', 'Helvetica Neue', sans-serif;
">
<div style="margin-bottom: 8px;">
<div style="font-weight: 700; color: #007bff; margin-bottom: 8px;">🔒 IRCC Security Checker</div>
<div style="font-size: 12px; color: #495057; margin-bottom: 10px;">
Captures tracker data on the overview or details page after login.
</div>
<button id="irccCheckBtn" style="display:block;width:100%;margin-bottom:8px;background:#007bff;color:#fff;border:none;border-radius:4px;padding:8px;cursor:pointer;">Check Security Status</button>
<button id="irccShowBtn" style="display:block;width:100%;margin-bottom:8px;background:#6c757d;color:#fff;border:none;border-radius:4px;padding:8px;cursor:pointer;">Show All Data</button>
<button id="irccCopyBtn" style="display:none;width:100%;margin-bottom:8px;background:#17a2b8;color:#fff;border:none;border-radius:4px;padding:8px;cursor:pointer;">Copy Data</button>
<button id="irccCloseBtn" style="display:block;width:100%;background:#dc3545;color:#fff;border:none;border-radius:4px;padding:8px;cursor:pointer;">Close</button>
<div id="irccStatusHint" style="font-size: 11px; color: #6c757d; margin-top: 6px;">Waiting for tracker data…</div>
</div>
<div id="irccResults" style="display:none;">
<div id="irccSecurity" style="margin-bottom:8px;"></div>
<pre id="irccData" style="display:none;margin:0;max-height:280px;overflow:auto;background:#f8f9fa;border:1px solid #e9ecef;padding:8px;border-radius:4px;font-size:12px;"></pre>
</div>
</div>
`;
document.body.appendChild(wrap);
const refs = getUIRefs();
refs.close.onclick = () => wrap.remove();
refs.check.onclick = handleCheck;
refs.show.onclick = handleShowAllToggle;
refs.copy.onclick = handleCopy;
const updateHint = () => {
const ok = hasHistoryArray(window.__IRCC_TRACKER_DATA);
refs.hint.textContent = ok ? 'Captured tracker data.' : 'Waiting for tracker data…';
};
updateHint();
window.addEventListener('ircc-tracker-data', updateHint);
document.addEventListener('readystatechange', updateHint);
}
function getUIRefs() {
const box = document.getElementById('ircc-security-checker');
return {
box,
check: box.querySelector('#irccCheckBtn'),
show: box.querySelector('#irccShowBtn'),
copy: box.querySelector('#irccCopyBtn'),
close: box.querySelector('#irccCloseBtn'),
hint: box.querySelector('#irccStatusHint'),
results: box.querySelector('#irccResults'),
security: box.querySelector('#irccSecurity'),
data: box.querySelector('#irccData')
};
}
function hasHistoryArray(obj) {
try {
return obj && obj.relations && Array.isArray(obj.relations) &&
obj.relations[0] && Array.isArray(obj.relations[0].history);
} catch { return false; }
}
function getTrackerData() { return hasHistoryArray(window.__IRCC_TRACKER_DATA) ? window.__IRCC_TRACKER_DATA : null; }
function getHistory(obj) { try { return obj?.relations?.[0]?.history || []; } catch { return []; } }
function getApp(obj) { try { return obj?.app || null; } catch { return null; } }
function findLatestSecurityEntry(history) {
const isSecurity = (k) => String(k || '').trim().toLowerCase() === 'security';
const candidates = history
.filter(h => isSecurity(h.key))
.map(e => ({ e, t: parseDate(e.dateCreated) || parseDate(e.dateLoaded) || new Date(0) }))
.sort((a,b) => b.t - a.t);
return candidates[0]?.e || null;
}
function deriveSecurityStatusStrict(tracker) {
const history = getHistory(tracker);
const app = getApp(tracker);
const sec = findLatestSecurityEntry(history);
if (!sec) {
return {
state: 'no_security_key',
date: parseDate(app?.lastUpdated) || latestDate(history),
message: 'Congrats! There is no "Security" entry. Your file looks to be moving under the normal background check process.'
};
}
const code = toInt(sec.actStatus);
if (code === 33) {
return {
state: 'security_finished',
date: parseDate(sec.dateCreated) || parseDate(sec.dateLoaded) || parseDate(app?.lastUpdated),
message: 'Good news! A "Security" entry exists but is finished. Your application should now continue under the normal process.'
};
}
if (code === 17) {
return {
state: 'security_in_progress',
date: parseDate(sec.dateCreated) || parseDate(sec.dateLoaded) || parseDate(app?.lastUpdated),
message: 'Heads up: A "Security" entry is present and in progress. This stage can be stressful and sometimes lengthy—stay strong. Hopefully it wraps up soon.'
};
}
return {
state: 'security_other',
date: parseDate(sec.dateCreated) || parseDate(sec.dateLoaded) || parseDate(app?.lastUpdated),
message: 'A "Security" entry is present. We can\'t map its status code precisely, but it likely indicates background processing. Stay positive—progress updates can take time.'
};
}
function handleCheck() {
const ui = getUIRefs();
ui.results.style.display = 'block';
const data = getTrackerData();
if (!data) {
ui.security.innerHTML = `
<div style="border-left:4px solid #6c757d;padding-left:8px;">
<div style="font-weight:600;color:#6c757d;">Tracker data not found yet.</div>
<div style="color:#6c757d;">Ensure you're logged in on overview or details.</div>
</div>`;
return;
}
const res = deriveSecurityStatusStrict(data);
const color = res.state === 'security_in_progress' ? SIGN_COLOR.negative
: res.state === 'security_finished' ? SIGN_COLOR.positive
: res.state === 'no_security_key' ? SIGN_COLOR.unknown
: SIGN_COLOR.neutral;
const label = res.state === 'security_in_progress' ? 'Security: In Progress'
: res.state === 'security_finished' ? 'Security: Finished'
: res.state === 'no_security_key' ? 'No "Security" Entry'
: 'Security: Other/Unknown';
const dateStr = res.date ? formatDate(res.date) : 'N/A';
ui.security.innerHTML = `
<div style="border-left:4px solid ${color};padding-left:8px;">
<div style="margin-bottom:4px;"><strong>Status</strong>: <span style="color:${color};font-weight:700;">${label}</span></div>
<div style="margin-bottom:4px;"><strong>Date</strong>: ${dateStr}</div>
<div style="color:#495057;">${res.message}</div>
</div>`;
}
function handleShowAllToggle() {
const ui = getUIRefs();
ui.results.style.display = 'block';
if (ui.data.style.display !== 'none' && ui.data.textContent.trim().length > 0) {
ui.data.style.display = 'none';
ui.copy.style.display = 'none';
return;
}
const data = getTrackerData();
if (!data) {
ui.data.textContent = 'Tracker data not found. Log in and reload the overview page.';
ui.data.style.display = 'block';
ui.copy.style.display = 'none';
return;
}
const history = getHistory(data);
const explained = history.map(h => explainHistoryEntry(h));
ui.data.textContent = JSON.stringify({ legend: historyLegend(), items: explained }, null, 2);
ui.data.style.display = 'block';
ui.copy.style.display = 'block';
}
function handleCopy() {
const ui = getUIRefs();
const txt = ui.data.textContent || '';
if (!txt) return;
copy(txt);
ui.copy.textContent = 'Copied!';
setTimeout(() => ui.copy.textContent = 'Copy Data', 900);
}
function explainHistoryEntry(h) {
const label = String(h.key ?? '').trim();
const labelKey = label.toLowerCase();
const meaningObj = KEY_CODE_MEANINGS[labelKey] || (isNumeric(label) ? KEY_CODE_MEANINGS[label] : null);
const sign = meaningObj?.sign || 'unknown';
const meaning = meaningObj?.meaning || (isNumeric(label) ? 'Internal IRCC code' : null);
const signNote = sign === 'positive' ? 'good sign'
: sign === 'neutral' ? 'routine/neutral'
: sign === 'negative' ? 'concerning'
: 'not confirmed';
return {
key: label || null,
key_meaning: meaning,
key_sign: sign,
key_sign_note: signNote,
actStatus: toInt(h.actStatus),
actStatus_note: actStatusNote(toInt(h.actStatus)),
actType: toInt(h.actType),
actType_note: actTypeHint(toInt(h.actType)),
dateCreated: h.dateCreated ?? null,
dateCreated_local: h.dateCreated ? formatDate(parseDate(h.dateCreated)) : null,
dateLoaded: h.dateLoaded ?? null,
dateLoaded_local: h.dateLoaded ? formatDate(parseDate(h.dateLoaded)) : null
};
}
function historyLegend() {
return {
key: 'Event label or internal code.',
key_meaning: 'Heuristic explanation or known meaning.',
key_sign: 'positive/neutral/unknown/negative; indicates whether the event is a good sign.',
actStatus: 'Numeric code if present; for "Security", 33=Finished, 17=In Progress.',
actType: 'Type/category of the event (varies).',
dateCreated: 'When IRCC recorded the event.',
dateLoaded: 'When the tracker loaded/synced it.'
};
}
function latestDate(list) {
let best = null;
for (const e of list || []) {
const d = parseDate(e?.dateCreated) || parseDate(e?.dateLoaded);
if (d && (!best || d > best)) best = d;
}
return best;
}
function isNumeric(s) { return /^\d+$/.test(String(s || '')); }
function actStatusNote(code) {
if (code == null || isNaN(code)) return null;
if (code === 33) return 'Finished/Completed';
if (code === 17) return 'In Progress';
return 'Other/Unknown';
}
function actTypeHint(t) {
if (t == null || isNaN(t)) return null;
if (t === 3) return 'Observed in some medical-related entries';
return null;
}
function parseDate(s) {
if (!s) return null;
const d = new Date(s);
return isNaN(d.getTime()) ? null : d;
}
function toInt(v) {
const n = parseInt(v, 10);
return isNaN(n) ? null : n;
}
function formatDate(d) {
try {
return new Intl.DateTimeFormat(undefined, {
year: 'numeric', month: 'short', day: '2-digit',
hour: '2-digit', minute: '2-digit'
}).format(d);
} catch {
return d?.toISOString?.() || '';
}
}
function copy(text) {
if (navigator.clipboard?.writeText) {
navigator.clipboard.writeText(text).catch(() => fallbackCopy(text));
} else {
fallbackCopy(text);
}
}
function fallbackCopy(text) {
const ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
try { document.execCommand('copy'); } catch {}
document.body.removeChild(ta);
}
function init() { createUI(); }
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init, { once: true });
} else {
init();
}
})();