/**
* CSRF Token Extractor for ServiceNow Background Scripts
*
* Extracts CSRF tokens from ServiceNow background script pages using multiple strategies.
* ServiceNow can embed tokens in various formats depending on instance configuration.
*/
/**
* Extract CSRF token from ServiceNow background script page HTML
*
* Uses multiple extraction strategies in order of preference:
* 1. JavaScript variable g_ck (most common)
* 2. Hidden input field sysparm_ck
* 3. Inline JavaScript sysparm_ck variable
* 4. Other common token patterns
*
* @param html - The HTML content from the background script page
* @returns The extracted CSRF token or null if not found
*/
export function extractCSRFToken(html: string): string | null {
if (!html || typeof html !== 'string') {
return null;
}
// Strategy 1: Extract from g_ck JavaScript variable
// Pattern: var g_ck = 'token_value';
const gckMatch = /g_ck\s*=\s*['"]([^'"]+)['"]/.exec(html);
if (gckMatch && gckMatch[1]) {
return gckMatch[1];
}
// Strategy 2: Extract from hidden input field sysparm_ck
// Pattern: <input name="sysparm_ck" value="token_value" />
const inputMatch = /<input[^>]*name=['"]sysparm_ck['"][^>]*value=['"]([^'"]+)['"]/.exec(html);
if (inputMatch && inputMatch[1]) {
return inputMatch[1];
}
// Strategy 3: Extract from inline JavaScript sysparm_ck variable
// Pattern: sysparm_ck = 'token_value';
const sysparmMatch = /sysparm_ck\s*=\s*['"]([^'"]+)['"]/.exec(html);
if (sysparmMatch && sysparmMatch[1]) {
return sysparmMatch[1];
}
// Strategy 4: Extract from any input with name containing 'ck'
// Pattern: <input name="*ck*" value="token_value" />
const ckMatch = /<input[^>]*name=['"][^'"]*ck[^'"]*['"][^>]*value=['"]([^'"]+)['"]/.exec(html);
if (ckMatch && ckMatch[1]) {
return ckMatch[1];
}
// Strategy 5: Look for any JavaScript variable ending with '_ck'
const jsCkMatch = /(\w+_ck)\s*=\s*['"]([^'"]+)['"]/.exec(html);
if (jsCkMatch && jsCkMatch[2]) {
return jsCkMatch[2];
}
return null;
}
/**
* Validate that an extracted token looks like a valid CSRF token
*
* @param token - The token to validate
* @returns True if the token appears valid
*/
export function isValidCSRFToken(token: string | null): boolean {
if (!token || typeof token !== 'string') {
return false;
}
// Basic validation: should be non-empty and not contain obvious invalid characters
if (token.trim().length === 0) {
return false;
}
// Should not contain HTML tags or obvious non-token content
if (token.includes('<') || token.includes('>') || token.includes('&')) {
return false;
}
// Should be reasonable length (ServiceNow tokens are typically 32-64 characters)
if (token.length < 8 || token.length > 128) {
return false;
}
return true;
}
/**
* Extract and validate CSRF token from HTML
*
* Combines extraction and validation in a single function
*
* @param html - The HTML content from the background script page
* @returns The validated CSRF token or null if not found or invalid
*/
export function extractAndValidateCSRFToken(html: string): string | null {
const token = extractCSRFToken(html);
return isValidCSRFToken(token) ? token : null;
}
/**
* Get debugging information about token extraction
*
* Useful for troubleshooting token extraction issues
*
* @param html - The HTML content from the background script page
* @returns Debug information about the extraction process
*/
export function debugTokenExtraction(html: string): {
foundTokens: string[];
extractionStrategies: {
gck: string | null;
inputSysparmCk: string | null;
jsSysparmCk: string | null;
anyCk: string | null;
jsCk: string | null;
};
isValid: boolean;
} {
const strategies = {
gck: /g_ck\s*=\s*['"]([^'"]+)['"]/.exec(html)?.[1] || null,
inputSysparmCk: /<input[^>]*name=['"]sysparm_ck['"][^>]*value=['"]([^'"]+)['"]/.exec(html)?.[1] || null,
jsSysparmCk: /sysparm_ck\s*=\s*['"]([^'"]+)['"]/.exec(html)?.[1] || null,
anyCk: /<input[^>]*name=['"][^'"]*ck[^'"]*['"][^>]*value=['"]([^'"]+)['"]/.exec(html)?.[1] || null,
jsCk: /(\w+_ck)\s*=\s*['"]([^'"]+)['"]/.exec(html)?.[2] || null,
};
const foundTokens = Object.values(strategies).filter(token => token !== null) as string[];
const finalToken = extractAndValidateCSRFToken(html);
return {
foundTokens,
extractionStrategies: strategies,
isValid: finalToken !== null,
};
}