Skip to content

Commit

Permalink
Convert to TypeScript
Browse files Browse the repository at this point in the history
  • Loading branch information
AdamWr committed Feb 19, 2024
1 parent cc713da commit a478c30
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,11 @@ import {
* @added unknown.
*/

export function hrefSanitizer(source, selector, attribute = 'text') {
export function hrefSanitizer(
source: Source,
selector: string,
attribute = 'text',
) {
if (!selector) {
logMessage(source, 'Selector is required.');
return;
Expand All @@ -109,61 +113,66 @@ export function hrefSanitizer(source, selector, attribute = 'text') {
/**
* Extracts text from an element based on the specified attribute.
*
* @param {Element} element - The element from which to extract the text.
* @param {string} attribute - The attribute indicating how to extract the text.
* @param {HTMLAnchorElement} anchor - The element from which to extract the text.
* @param {string} attr - The attribute indicating how to extract the text.
* @returns {string} The extracted text.
*/
const extractText = (element, attribute) => {
if (attribute === 'text') {
return element.textContent
.replace(regexpNotValidAtStart, '')
.replace(regexpNotValidAtEnd, '');
const extractNewHref = (anchor: HTMLAnchorElement, attr: string): string => {
if (attr === 'text') {
return anchor.textContent
? anchor.textContent.replace(regexpNotValidAtStart, '')
.replace(regexpNotValidAtEnd, '')
: '';
}
if (attribute.startsWith('?')) {
if (attr.startsWith('?')) {
try {
const url = new URL(element.href, document.location);
return url.searchParams.get(attribute.slice(1)) || '';
const url = new URL(anchor.href, document.location.href);
return url.searchParams.get(attr.slice(1)) || '';
} catch (ex) {
logMessage(
source,
`Cannot retrieve the parameter '${attr.slice(1)}' from the URL '${anchor.href}`,
);
return '';
}
}
if (attribute.startsWith('[') && attribute.endsWith(']')) {
return element.getAttribute(attribute.slice(1, -1)) || '';
if (attr.startsWith('[') && attr.endsWith(']')) {
return anchor.getAttribute(attr.slice(1, -1)) || '';
}
return '';
};

/**
* Validates a URL, if valid return URL,
* otherwise return empty string.
* otherwise return null.
*
* @param {string} text - The URL to be validated
* @returns {string} - URL
* @returns {string} - URL for valid URL, otherwise null.
*/
const validateURL = (text) => {
const getValidURL = (text: string): string | null => {
if (!text) {
return '';
return null;
}
try {
const { href } = new URL(text, document.location);
const { href } = new URL(text, document.location.href);
return href;
} catch {
return '';
return null;
}
};

/**
* Sanitizes the href attribute of elements matching the given selector.
*
* @param {string} selector - The CSS selector to match the elements.
* @param {string} elem - The CSS selector to match the elements.
* @returns {void}
*/
const sanitize = (selector) => {
const sanitize = (elem: string) => {
let elements;
try {
elements = document.querySelectorAll(selector);
elements = document.querySelectorAll(elem);
} catch (e) {
logMessage(source, `Failed to find elements matching selector "${selector}"`);
logMessage(source, `Failed to find elements matching selector "${elem}"`);
return;
}

Expand All @@ -173,17 +182,17 @@ export function hrefSanitizer(source, selector, attribute = 'text') {

for (let index = 0; index < elements.length; index += 1) {
try {
const element = elements[index];
const element = elements[index] as HTMLAnchorElement;
if (element.nodeName.toLowerCase() !== 'a' || !element.hasAttribute('href')) {
continue;
}
const href = element.getAttribute('href');
const text = extractText(element, attribute);
const validatedHref = validateURL(text);
if (validatedHref === '' || validatedHref === href) {
const newHref = extractNewHref(element, attribute);
const newValidHref = getValidURL(newHref);
if (!newValidHref) {
logMessage(source, `Invalid URL: ${newHref}`);
continue;
}
element.setAttribute('href', text);
element.setAttribute('href', newValidHref);
} catch (ex) {
logMessage(source, `Failed to sanitize ${elements[index]}.`);
}
Expand Down
39 changes: 32 additions & 7 deletions tests/scriptlets/href-sanitizer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ test('Checking if alias name works', (assert) => {

test('Santize href - text content', (assert) => {
const expectedHref = 'https://example.org/';
const elem = createElem('https://tracker.com/foo?redirect=https%3A%2F%2Fexample.org%2F', expectedHref);
const elem = createElem('https://example.com/foo?redirect=https%3A%2F%2Fexample.org%2F', expectedHref);
const selector = 'a[href*="?redirect="]';

const scriptletArgs = [selector];
Expand Down Expand Up @@ -86,20 +86,20 @@ test('Santize href - text content, create element after running scriptlet', (ass

test('Santize href - text content special characters', (assert) => {
const expectedHref = 'https://example.com/search?q=łódź';
const elem = createElem('https://tracker.com/foo', expectedHref);
const selector = 'a[href*="//tracker.com"]';
const elem = createElem('https://example.org/foo', expectedHref);
const selector = 'a[href*="//example.org"]';

const scriptletArgs = [selector];
runScriptlet(name, scriptletArgs);

assert.strictEqual(elem.getAttribute('href'), expectedHref, 'href has been sanitized');
assert.strictEqual(decodeURIComponent(elem.getAttribute('href')), expectedHref, 'href has been sanitized');
assert.strictEqual(window.hit, 'FIRED');
});

test('Santize href - text content, Twitter like case', (assert) => {
const elem = createElem('https://tracker.com/foo', 'https://agrd.io/promo_turk_83off…'); // Link from Twitter/X
const elem = createElem('https://example.com/foo', 'https://agrd.io/promo_turk_83off…'); // Link from Twitter/X
const expectedHref = 'https://agrd.io/promo_turk_83off';
const selector = 'a[href*="//tracker.com"]';
const selector = 'a[href*="//example.com"]';

const scriptletArgs = [selector];
runScriptlet(name, scriptletArgs);
Expand All @@ -109,7 +109,7 @@ test('Santize href - text content, Twitter like case', (assert) => {
});

test('Santize href - query parameter 1', (assert) => {
const elem = createElem('https://tracker.com/foo?redirect=https://example.org/');
const elem = createElem('https://example.com/foo?redirect=https://example.org/');
const expectedHref = 'https://example.org/';
const selector = 'a[href*="?redirect="]';
const attr = '?redirect';
Expand Down Expand Up @@ -146,3 +146,28 @@ test('Santize href - get href from attribute', (assert) => {
assert.strictEqual(elem.getAttribute('href'), expectedHref, 'href has been sanitized');
assert.strictEqual(window.hit, 'FIRED');
});

test('Santize href - invalid URL', (assert) => {
const expectedHref = 'https://foo.com/bar';
const elem = createElem(expectedHref, 'https://?');
const selector = 'a[href="https://foo.com/bar"]';

const scriptletArgs = [selector];
runScriptlet(name, scriptletArgs);

assert.strictEqual(elem.getAttribute('href'), expectedHref, 'href has not been changed');
assert.strictEqual(window.hit, 'FIRED');
});

test('Santize href - parameter, invalid URL', (assert) => {
const expectedHref = 'https://?example.com/foo?redirect=https://example.org/';
const elem = createElem(expectedHref);
const selector = 'a[href*="?redirect="]';
const attr = '?redirect';

const scriptletArgs = [selector, attr];
runScriptlet(name, scriptletArgs);

assert.strictEqual(elem.getAttribute('href'), expectedHref, 'href has not been changed');
assert.strictEqual(window.hit, 'FIRED');
});

0 comments on commit a478c30

Please sign in to comment.