From b21f974e1f58b0da77bdc90d72bda0351436003c Mon Sep 17 00:00:00 2001 From: Abdullah Arif Date: Sun, 29 Sep 2024 14:46:33 +0500 Subject: [PATCH 1/3] feat(isSIP): check if string is a SIP --- README.md | 1 + src/index.js | 3 ++ src/lib/isSIP.js | 80 +++++++++++++++++++++++++++++++++++ test/validators/isSIP.test.js | 42 ++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 src/lib/isSIP.js create mode 100644 test/validators/isSIP.test.js diff --git a/README.md b/README.md index 7c1cadb9f..63ee849f7 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,7 @@ Validator | Description **isVAT(str, countryCode)** | check if the string is a [valid VAT number][VAT Number] if validation is available for the given country code matching [ISO 3166-1 alpha-2][ISO 3166-1 alpha-2].

`countryCode` is one of `['AL', 'AR', 'AT', 'AU', 'BE', 'BG', 'BO', 'BR', 'BY', 'CA', 'CH', 'CL', 'CO', 'CR', 'CY', 'CZ', 'DE', 'DK', 'DO', 'EC', 'EE', 'EL', 'ES', 'FI', 'FR', 'GB', 'GT', 'HN', 'HR', 'HU', 'ID', 'IE', 'IL', 'IN', 'IS', 'IT', 'KZ', 'LT', 'LU', 'LV', 'MK', 'MT', 'MX', 'NG', 'NI', 'NL', 'NO', 'NZ', 'PA', 'PE', 'PH', 'PL', 'PT', 'PY', 'RO', 'RS', 'RU', 'SA', 'SE', 'SI', 'SK', 'SM', 'SV', 'TR', 'UA', 'UY', 'UZ', 'VE']`. **isWhitelisted(str, chars)** | check if the string consists only of characters that appear in the whitelist `chars`. **matches(str, pattern [, modifiers])** | check if the string matches the pattern.

Either `matches('foo', /foo/i)` or `matches('foo', 'foo', 'i')`. +**isSIP(str)** | check if the string is a [SIP](https://www.ietf.org/rfc/rfc3261.txt). ## Sanitizers diff --git a/src/index.js b/src/index.js index 1bc65a886..dfdd5ecaf 100644 --- a/src/index.js +++ b/src/index.js @@ -129,6 +129,8 @@ import isStrongPassword from './lib/isStrongPassword'; import isVAT from './lib/isVAT'; +import isSIP from './lib/isSIP'; + const version = '13.12.0'; const validator = { @@ -243,6 +245,7 @@ const validator = { isLicensePlate, isVAT, ibanLocales, + isSIP, }; export default validator; diff --git a/src/lib/isSIP.js b/src/lib/isSIP.js new file mode 100644 index 000000000..bc694aa84 --- /dev/null +++ b/src/lib/isSIP.js @@ -0,0 +1,80 @@ +import assertString from './util/assertString'; + +/* Regular expression to match the SIP URI structure */ +function isValidSipUri(uri) { + const sipUriRegex = /^sip:([^:@]+(:[^@]*)?@)?((([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}|(\d{1,3}\.){3}\d{1,3}|\[[0-9a-fA-F:]+\])(:\d{1,5})?)(;([a-zA-Z0-9-]+=[^;?]+|lr))*?(\?[^#]*)?$/; + const sipsUriRegex = /^sips:([^:@]+(:[^@]*)?@)?((([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}|(\d{1,3}\.){3}\d{1,3}|\[[0-9a-fA-F:]+\])(:\d{1,5})?)(;([a-zA-Z0-9-]+=[^;?]+|lr))*?(\?[^#]*)?$/; + + return sipUriRegex.test(uri) || sipsUriRegex.test(uri); +} + +function isValidScheme(schemeAndUser) { + return ( + schemeAndUser.startsWith('sip:') || schemeAndUser.startsWith('sips:') + ); +} + +function isValidPort(domain) { + const portMatch = domain.match(/:(\d+)($|;)/); + if (portMatch) { + const port = parseInt(portMatch[1], 10); + console.log(port); + return !isNaN(port) && port > 0 && port <= 65535; + } + return true; +} + +function isValidParameters(uri) { + if (uri.includes(';')) { + const params = uri.split(';').slice(1); + for (const param of params) { + /* + - key is a valid parameter + - key=value is a valid parameter + - key=value1;key=value2;... is a valid parameter + */ + const paramPart = param.split('?')[0]; + if (!/^[a-zA-Z0-9-]+(=[a-zA-Z0-9_.!~*'();&=+$,%-]*)?$/.test(paramPart)) { + return false; + } + } + } + return true; +} + + +/** + * Function to validate if a string is a valid SIP URI + * @param {String} str - The SIP URI string + * @returns {Boolean} - Returns true if valid, otherwise false + */ +export default function isSIP(str) { + assertString(str); + + // First check the structure using regex + if (!isValidSipUri(str)) { + return false; + } + + // Split the URI into parts (scheme, user, domain) + const parts = str.split('@'); + const domain = parts.pop(); + const schemeAndUser = parts.join('@'); + + // Check for valid scheme (sip or sips) + if (!isValidScheme(schemeAndUser)) { + return false; + } + + // Check if there's a port + if (!isValidPort(domain)) { + return false; + } + + // Validate parameters if present + if (!isValidParameters(str)) { + return false; + } + + return true; +} diff --git a/test/validators/isSIP.test.js b/test/validators/isSIP.test.js new file mode 100644 index 000000000..bff28a27b --- /dev/null +++ b/test/validators/isSIP.test.js @@ -0,0 +1,42 @@ +import test from '../testFunctions'; + +describe('isSIP', () => { + it('should validate SIP URIs', () => { + test({ + validator: 'isSIP', + valid: [ + 'sip:john.doe@example.com', + 'sips:alice@secure.com', + 'sip:user@domain.com:5060', + 'sip:user@domain.com;transport=tcp', + 'sips:user@example.com:5060', + 'sips:user@secure.com;transport=tcp', + 'sip:username@domain.com', + 'sip:username@sub.domain.com', + 'sip:alice:password@atlanta.com', + 'sip:username@192.168.1.1', + 'sips:user@192.168.1.1:5061;transport=tls', + 'sip:username@domain.com;user=phone', + 'sip:username@domain.com;method=INVITE', + 'sip:username@domain.com?subject=test', + 'sip:username@domain.com;tag=1234', + 'sips:username@domain.com;secure=true', + 'sip:username@domain.com:5060;lr', + 'sip:username:password@host.com:5060;transport=udp?header=value', + ], + invalid: [ + 'https://www.example.com', + 'sip:user', + 'sips:@invalid.com', + 'sip:username@-example.com', + 'sip:user@domain.com:abc', + 'sip:user@domain.com;user=phone;tag=', + 'sips:user@domain:5060@otherdomain.com', + 'sip:@', + 'sip:user@.com', + 'sip:user@domain..com', + 'sips:username@domain.com:port', + ], + }); + }); +}); From d00c7f7db8b04fe723a204b123fa2a79ef5f7d73 Mon Sep 17 00:00:00 2001 From: Abdullah Arif <102965181+aabdullaharif@users.noreply.github.com> Date: Sun, 29 Sep 2024 14:56:54 +0500 Subject: [PATCH 2/3] fix(isSIP): remove console.log from isSIP.js --- src/lib/isSIP.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/isSIP.js b/src/lib/isSIP.js index bc694aa84..da0ffb4a6 100644 --- a/src/lib/isSIP.js +++ b/src/lib/isSIP.js @@ -18,7 +18,6 @@ function isValidPort(domain) { const portMatch = domain.match(/:(\d+)($|;)/); if (portMatch) { const port = parseInt(portMatch[1], 10); - console.log(port); return !isNaN(port) && port > 0 && port <= 65535; } return true; From 8bd0e8d602d23673b2b6a88d83280194d0c38910 Mon Sep 17 00:00:00 2001 From: Abdullah Arif <102965181+aabdullaharif@users.noreply.github.com> Date: Mon, 30 Sep 2024 11:42:45 +0500 Subject: [PATCH 3/3] fix(isSIP): remove redundancy --- src/lib/isSIP.js | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/lib/isSIP.js b/src/lib/isSIP.js index da0ffb4a6..3317c53ba 100644 --- a/src/lib/isSIP.js +++ b/src/lib/isSIP.js @@ -2,16 +2,8 @@ import assertString from './util/assertString'; /* Regular expression to match the SIP URI structure */ function isValidSipUri(uri) { - const sipUriRegex = /^sip:([^:@]+(:[^@]*)?@)?((([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}|(\d{1,3}\.){3}\d{1,3}|\[[0-9a-fA-F:]+\])(:\d{1,5})?)(;([a-zA-Z0-9-]+=[^;?]+|lr))*?(\?[^#]*)?$/; - const sipsUriRegex = /^sips:([^:@]+(:[^@]*)?@)?((([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}|(\d{1,3}\.){3}\d{1,3}|\[[0-9a-fA-F:]+\])(:\d{1,5})?)(;([a-zA-Z0-9-]+=[^;?]+|lr))*?(\?[^#]*)?$/; - - return sipUriRegex.test(uri) || sipsUriRegex.test(uri); -} - -function isValidScheme(schemeAndUser) { - return ( - schemeAndUser.startsWith('sip:') || schemeAndUser.startsWith('sips:') - ); + const sipUriRegex = /^sips?:([^:@]+(:[^@]*)?@)?((([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}|(\d{1,3}\.){3}\d{1,3}|\[[0-9a-fA-F:]+\])(:\d{1,5})?)(;([a-zA-Z0-9-]+=[^;?]+|lr))*?(\?[^#]*)?$/; + return sipUriRegex.test(uri); } function isValidPort(domain) { @@ -58,12 +50,6 @@ export default function isSIP(str) { // Split the URI into parts (scheme, user, domain) const parts = str.split('@'); const domain = parts.pop(); - const schemeAndUser = parts.join('@'); - - // Check for valid scheme (sip or sips) - if (!isValidScheme(schemeAndUser)) { - return false; - } // Check if there's a port if (!isValidPort(domain)) {