{"id":26328598,"url":"https://github.com/codesplinta/urisanity","last_synced_at":"2025-08-18T21:14:30.085Z","repository":{"id":57387535,"uuid":"379757914","full_name":"codesplinta/URISanity","owner":"codesplinta","description":"sanitize uris in web and web-like applications with confidence","archived":false,"fork":false,"pushed_at":"2024-09-19T01:19:38.000Z","size":211,"stargazers_count":22,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-07-23T12:22:26.735Z","etag":null,"topics":["uri-sanitization","uri-sch"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/codesplinta.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-06-24T00:05:09.000Z","updated_at":"2025-03-06T18:20:05.000Z","dependencies_parsed_at":"2024-03-16T11:46:58.522Z","dependency_job_id":"33c64157-ed4c-408c-af4a-f0e544dafecb","html_url":"https://github.com/codesplinta/URISanity","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/codesplinta/URISanity","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codesplinta%2FURISanity","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codesplinta%2FURISanity/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codesplinta%2FURISanity/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codesplinta%2FURISanity/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/codesplinta","download_url":"https://codeload.github.com/codesplinta/URISanity/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codesplinta%2FURISanity/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271061403,"owners_count":24692524,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-18T02:00:08.743Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["uri-sanitization","uri-sch"],"created_at":"2025-03-15T21:17:49.148Z","updated_at":"2025-08-18T21:14:30.053Z","avatar_url":"https://github.com/codesplinta.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![@isocroft](https://img.shields.io/badge/@isocroft-CodeSplinta-blue) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-cyan.svg?style=flat-square)](http://makeapullrequest.com) [![Made in Nigeria](https://img.shields.io/badge/made%20in-nigeria-008751.svg?style=flat-square)](https://github.com/acekyd/made-in-nigeria)\n\n# URI Sanity\n\nA small library used in the Browser and NodeJS to vet URIs (to mitigate vulnerabilities) with confidence. In other words, It's the [DOMPurify](https://www.github.com/cure53/DOMPurify) for URIs. A uniform resource locator (URL) is, in fact, a subset of [uniform resource identifiers](https://whatis.techtarget.com/definition/URI-Uniform-Resource-Identifier?_gl=1*7pixwg*_ga*NDg2NjQ5NTIxLjE2NDQ3MTE5NjA.*_ga_TQKE4GS5P9*MTY0NDcxMTk1OC4xLjAuMTY0NDcxMTk1OC4w\u0026_ga=2.95812444.1772810844.1644711960-486649521.1644711960) (URI). Therefore, this library covers the super set of all resource identifiers where possible.\n\n## Motivation\n\nThere are many web-based zero-day vulnerabilities that can be expolited in Browsers/NodeJS servers using Standard and/or Custom URI schemes. Certain browsers like Safari and Firefox are usually subceptible to launching such URIs without a prompt or restrictions and enable [Arbitrary File Execution](https://en.wikipedia.org/wiki/Arbitrary_code_execution#:~:text=arbitrary%20code%20execution%20(ACE)%20is%20an%20attacker's%20ability%20to%20run%20any%20commands%20or%20code%20of%20the%20attacker's%20choice%20on%20a%20target%20machine%20or%20in%20a%20target%20process.), [Remote Code Execution](https://www.checkpoint.com/cyber-hub/cyber-security/what-is-remote-code-execution-rce/#:~:text=Remote%20code%20execution%20(RCE)%20attacks,control%20over%20a%20compromised%20machine.) and/or [Connection String Pollution](https://link.springer.com/chapter/10.1007/978-3-642-16120-9_16?noAccess=true) (on the server) where possible. This is why this library was built. It moves to create a layer of protection for your web applications both on the Browser and on the Server (NodeJS only) by blocking badly formed/suspicious URIs.\n\nFurthermore, other solutions like [braintree/sanitize-url](https://www.github.com/braintree/sanitize-url) are quite naive and a bit too specific in [it's approach](https://github.com/braintree/sanitize-url/issues/14) to URL sanitization. Also, most web front-end frameworks like **Angular** and **Vue** (safe for **React**) do not do a very robust and serious (non-trivial) job of sanitiziting URLs either. This is why this library is very important to web application developers who need reliability in sanitizing URLs.\n\n## Validation\n\nThis library has been validated against popular malicious URIs delineated [here](https://book.hacktricks.xyz/pentesting-web/xss-cross-site-scripting) and [here](https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html)\n\n## Installation\n\nInstall using `npm`\n\n```bash\nnpm install urisanity\n```\n\n or install using `yarn`.\n\n```bash\nyarn add urisanity\n```\n\n\n## Getting Started\n\nAll you need to do is import the package appropriately depending on the environment (Browser OR Node) being used\n\n### Browser environment\n\n\u003e Using a `script` tag directly inside a web page\n\n```html\n\u003cscript type=\"text/javascript\" src=\"https://unpkg.com/browse/urisanity@0.1.6/dist/urisanity.min.js\" crossorigin=\"anonymous\"\u003e\u003c/script\u003e\n```\n\n\u003e import as ES6 module - no setup required\n\n```js\nimport URISanity from 'urisanity';\n\nconst sanitizedUrl = URISanity.vet('blob:https://www.foo-.evil.com/undefined', {\n  // All flag options set - valid\n  allowScriptOrDataURI: false,\n  allowFileSystemURI: false,\n  allowCommsAppURI: true,\n  allowDBConnectionStringURI: false,\n  allowBrowserSpecificURI: false,\n  allowWebTransportURI: false,\n  allowServiceAPIURI: false,\n});\n\nconsole.log(sanitizedUrl); // \"about:blank\"\n\nconst sanitizedDBUri = URISanity.vet(\"jdbc:sqlserver://;servername=server_name;integratedSecurity=true;authenticationScheme=JavaKerberos\", {\n  // One flag option set - valid\n  allowDBConnectionStringURI: true,\n  allowFileSystemURI: false, // you can omit this since it's `false`\n  allowCommsAppURI: false, // you can omit this since it's `false`\n  allowScriptOrDataURI: false, // you can omit this since it's `false`\n  allowWebTransportURI: false, // you can omit this since it's `false`\n  allowServiceAPIURI: false // you can omit this since it's `false`\n})\n\nconsole.log(sanitizedDBUri) // \"jdbc:sqlserver://;servername=server_name;integratedSecurity=true;authenticationScheme=JavaKerberos\"\n\nconst sanitizedCustomUrl = URISanity.vet(\n  'icloud-sharing://www.icloud.com/photos/01eFfrthOPvnfZqlKMn', {\n    /* No flag options set - valid */\n});\n\nconsole.log(sanitizedCustomUrl); // \"about:blank\"\n\nconst santizedBadUrl = URISanity.vet('http://aa.com/\u003c/script\u003e\"\u003e\u003cimg src=x onerror=\"prompt(document.domain)\"\u003e)', {\n  allowWebTransportURI: true,\n  allowScriptOrDataURI: true\n})\n\nconsole.log(sanitizedBadUrl); // \"about:blank\"\n\n\n\nconst paramValue = URISanity.extractParamValueFromUri(\n  'https://www.example.com?xyz=%200000#intro',\n  'xyz'\n);\n\nconsole.log(paramValue); // \" 0000\"\n\nconst checkPassed = URISanity.checkParamsOverWhiteList(\n  'grpc://api.broker.rt-msg.io:443?user=sal%C3%A1ta',\n  ['user']\n);\n\nconsole.log(checkPassed); // true\n\nconst isSame = URISanity.isSameOrigin(window.location.href)\n\nconsole.log(isSame) // true\n```\n\n### NodeJS (commonjs) environment\n\n\u003e Setup an env file in your NodeJS app and include an `ORIGIN`\n\n```.env\nORIGIN=http://127.0.0.1:4050\n```\n\n```js\nconst URISanity = require('urisanity');\n\nconst sanitizedFileUrl = URISanity.vet(\n  'file://www.airbnb.com/Users/xxx/Desktop/index.html',\n  {\n    allowWebTransportURI: true\n  }\n);\n\nconsole.log(sanitizedFileUrl) // \"about:blank\"\n```\n\n```js\nconst URISanity = require('urisanity');\n\nlet sanitizedUrl = URISanity.vet(\n  'file://www.airbnb.com/Users/xxx/Desktop/index.html',\n  {\n    allowWebTransportURI: false,\n    allowFileSystemURI: true\n  }\n);\n\nconsole.log(sanitizedUrl) // \"file://www.airbnb.com/Users/xxx/Desktop/index.html\"\n```\n## Implementing Trusted Types\n\n\u003e You can make use of [**Trusted Types**](https://w3c.github.io/webappsec-trusted-types/dist/spec/#trused-script-url) while using **URI Sanity**. An excerpt from a [_**2021 report from Google on Trusted Types**_](https://storage.googleapis.com/pub-tools-public-publication-data/pdf/2cbfffc0943dabf34c499f786080ffa2cda9cb4c.pdf) reads:\n\n_Trusted Types are supported in several popular frameworks and libraries including\nAngular, React (with a feature flag), Lit, Karma, and Webpack. Enforcing Trusted Types\nin applications built on top of these frameworks is now [relatively simple](https://auth0.com/blog/securing-spa-with-trusted-types/); in some cases\nno application-level code changes are required._\n\nBefore the advent of **Trusted Types** (specifically, in the days of Angular 1.x), frontend web engineers used [this approach](https://stackoverflow.com/questions/15606751/angularjs-changes-urls-to-unsafe-in-extension-page) in sanitizing URIs for web applications and it was grossly inefficient and/or naive. This is also [another approach](https://github.com/angular/angular/blob/master/packages/core/src/sanitization/url_sanitizer.ts#L36) that still doesn't cater to a much braoder system for URI sanitization. Now, with **URISanity**, you have the broader systems needed for quality URI sanitization.\n\n```js\nimport URISanity from 'urisanity';\nimport DOMPurify from 'dompurify';\n\nwindow.addEventListener('securitypolicyviolation', console.error.bind(console));\n\n/* @HINT: feature / object detection */\nif (typeof window.trustedTypes !== 'undefined') {\n  trustedTypes.createPolicy('default', {\n    createHTML: (html) =\u003e {\n      /* @HINT: \n        \n        sanitize all potentially malicious characters from HTML string \n      */\n      return DOMPurify.sanitize(html, {\n        USE_PROFILES: {\n          html: true,\n          svg: true,\n        },\n      })\n    },\n    createScriptURL: (url) =\u003e {\n      /* @HINT: \n        \n        vet URL string and return \"about:blank\" if URL string is suspicious\n      */\n      return URISanity.vet(url, {\n        allowWebTransportURI: true,\n      })\n    },\n  });\n}\n```\n\n## More Use Cases\n\n**URISanity** can be used to improve the web security of browser API sinks (injection sinks) that make use if URIs and aren't covered and/or catered for by **Trusted Types** and basic **CSP**. By instrumenting these API sinks (sinks for Document Object Model / Browser Object Model) and utilizing browser custom event API(s), the solution is quite elegant. Take a look below:\n\n\u003eLet's define some basic browser custom events and their handler\n\n```javascript\n/* @NOTE: Can also be the \"connect-src\" whitelist of urls from a CSP directive */\n/* @HINT: The whitelist below is for excluding URLs that are not known to the web app and may be expoitative/suspicious */\nconst whitelistedURLEndpoints = [\n  'https://www.facebook.com/tr',\n  'https://www.google-analytics.com/collect'\n]\n\n/* @HINT: Setting custome events to check and validate URLs */\ndocument.addEventListener( 'beforerequest', onBeforeURIUsed, false )\ndocument.addEventListener( 'beforeinclude', onBeforeURIUsed, false )\n\n/* @HINT: Event handler common to the two events above */\nfunction onBeforeURIUsed ( event ) {\n  /* @CHECK: https://www.npmjs.com/package/urisanity ; urisanity */\n  /* @HINT: Vet the URL endpoint being requested/included for safety */\n  if (window.urisanity.vet(\n    event.detail.endpoint,\n    { allowWebTransportURI: true }\n  ) !== 'about:blank') {\n    const { origin, pathname } = new URL(event.detail.endpoint)\n\n    /* @HINT: Make sure the endpoint being requested/included is part of the whitelist */\n    if (whitelistedURLEndpoints.includes(`${origin}${pathname}`)) {\n      if (origin.includes('.google-analytics.')) {\n        /* @HINT: Check that only the request params we need are attached */\n        /* @HINT: Any other extra params should not be allowed */\n        if (window.urisanity.checkParamsOverWhiteList(\n          event.detail.endpoint,\n          ['tid', 'cid'],\n          event.detail.data\n        )) {\n          return;\n        }\n      }\n    }\n  }\n\n  /* @HINT: trigger an error to be thrown when the endpoint is not in the whitelist above */\n  /* @HINT: Or the validation above for any origin (or for google-analytics) doesn't pass */\n  event.preventDefault()\n}\n```\n\u003eNow, let's instrument certain browser API sinks and make them able to throw errors on suspicion of a malformed/malicious API.\n\n```javascript\n /*!\n  * FIRST SECTION\n  *\n  */\n\n/* @HINT: Extract the native definitions of these APIs from the DOM Interfaces */\nconst originalSetAttributeMethod = HTMLElement.prototype.setAttribute\n\n/* @HINT: Create a new definition for `setAttribute` that instruments the API to detect suspicious URIs */\nHTMLElement.prototype.setAttribute = function setAttribute (attributeName, newValue) {\n\t  const that = this;\n\t  const previousValue = that.getAttribute(attributeName);\n\n\t  const timerID = window.setTimeout(function () { \n      /* @HINT: Stop [ DOMSubtreeModified ] event from firing before [ DOMAttrModified ] event */\n\t\t  originalSetAttributeMethod.call(that, attributeName, newValue);\n\t  }, 0);\n\n    /* @HINT: Whenever the attribute name is `href`, then check the URL that is the value */\n    if (attributeName === 'href') {\n      /* @HINT: Fire a custom event `beforeinclude` to track manual whitelisting of URL endpoints */\n      let event = new window.CustomEvent('beforeinclude', {\n        detail: {\n          endpoint: newValue,\n          sink: \"HTMLElement.setAttribute\",\n          data: null\n        },\n        bubbles: true,\n        cancelable: true\n      });\n\n      /* @HINT: Detect if the dispatched custom event was cancelled by a call to `event.preventDefault()` */\n      /* @HINT: If the event was cancelled, it means the URL endpoint above was disallowed by the checks */\n      const cancelled = !document.dispatchEvent(event)\n\n       /* @HINT: If it's cancelled, stop the `setTimeout` call above from being executed by clearing the timeout */\n       /* @HINT: Also, we throw an error to stop the call to `setAttribute` from being requested */\n       if (cancelled) {\n         window.clearTimeout(timerID)\n         throw new Error(\n           \"Suspicious Activity: \"\n           +\n           event.detail.endpoint\n           +\n           \" request, using [ \" + event.detail.data + \" ] in \"\n           +\n           \" [ \" + event.detail.sink + \" ]\"\n         )\n       }\n    }\n\n    /* @HINT: When listening to mutation events, might be okay to stagger certain event sequences properly */\n\t  if (newValue !== previousValue) {\n\t    let event = document.createEvent(\"MutationEvent\");\n\t    event.initMutationEvent(\n\t      \"DOMAttrModified\",\n\t      true,\n\t      false,\n\t      that,\n\t      previousValue || \"\",\n\t      newValue || \"\",\n\t      attributeName,\n\t      (previousValue === null) ? event.ADDITION : event.MODIFICATION\n\t    );\n\t\t  \n\t    that.dispatchEvent(\n        event\n      );\n\t  }\n\t};\n\n\n /*!\n  * NEXT SECTION\n  *\n  */\n\n\n/* @HINT: craete a function/constructor that does nothing a.k.a no-operation function */\nconst noop = function noOperation () {}\n\n/* @HINT: Copy out the user-agent interface function `sendBeacon` */\nconst NativeSendBeacon = window.Navigator.prototype.sendBeacon || noop\n\nwindow.Navigator.prototype.sendBeacon = function sendBeacon (url, data) {\n  /* @HINT: Fire a custom event `beforerequest` to track manual whitelisting of URL endpoints */\n  const event = new window.CustomEvent('beforerequest', {\n    detail: {\n      endpoint: url,\n      method: \"POST\",\n      sink: \"Navigator.sendBeacon\",\n      data: data\n    },\n    bubbles: true,\n    cancelable: true\n  })\n\n  /* @HINT: Detect if the dispatched custom event was cancelled by a call to `event.preventDefault()` */\n  /* @HINT: If the event was cancelled, it means the URL endpoint above was disallowed by the checks */\n  const cancelled = !document.dispatchEvent(event)\n\n   /* @HINT: If it's cancelled, we throw an error to stop the call to `sendBeacon` from being requested */\n   if (cancelled) {\n     throw new Error(\n       \"Suspicious Activity: \"\n       +\n       event.detail.endpoint\n       +\n       \" request, using [ \" + event.detail.data + \" ] in \"\n\t      +\n\t      \" [ \" + event.detail.sink + \" ]\"\n     )\n   }\n\n   /* @HINT: If all checks out and no error was thrown above then proceed as usual */\n   return NativeSendBeacon.call(this, url, data);\n};\n\n/* @HINT: define property `name` on custom function */\n  Object.defineProperty(sendBeacon, 'name', {\n    writable: false,\n    value: 'sendBeacon'\n  });\n\n/* @HINT: define property function `toString` on custom function */\nObject.defineProperty(sendBeacon, 'toString', {\n  writable: true,\n  value: function toString () {\n    return NativeSendBeacon.toString()\n  }\n})\n\n/* @HINT: Take care of the special Firefox/IceWeasel (Gecko) property `toSource` */\nif ('toSource' in NativeSendBeacon) {\n  Object.defineProperty(sendBeacon, 'toSource', {\n    writable: true,\n    value: function toSource () {\n      return NativeSendBeacon.toSource()\n    }\n  })\n}\n```\n\nFinally, the code above in the event handler get triggered whenever `navigator.sendBeacon()` is called and the URLs are using **URISanity**. The [zhorn](https://www.npmjs.com/package/zhorn) package provides all of this functionality and depends on **URISanity**.\n\n## Documentation\n\nHere is a brief guide to using this library and it's API method(s)\n\n### Flag Options\n\u003eWhen using the `.vet(uri: String [, options: Object])` API method, there are flag option(s) to filter out different URI categories in the vetting process. They are as follows:\n\n1. **allowCommsAppURI** : This applies only to the deep links that are used by communication tools and apps like Whatsapp, Zoom, Slack, Skype or browser comms URIs e.g. _sms, tel, whatsapp, slack_\n2. **allowDBConnectionStringURI** :  This applies only to database connection string URIs e.g. _postgresql, mongodb, jdbc:mysql_\n3. **allowBrowserSpecificURI** : This applies only to browser extensions, packaged apps and browser data display URIs e.g. _view-source, moz-extension_\n4. **allowServiceAPIURI** : This appies only to third-party data storage services whos have specialized URIs for data transfer and storage operations e.g. _cloudinary, s3, grpc_\n5. **allowScriptOrDataURI** : This applies only to script and/or data URIs e.g. _data, javascript_\n6. **allowFileSystemURI** : This applies only to URIs related to the local filesystem e.g. _blob, file, local_\n7. **allowWebTransportURI** : This applies only to web data transport URIs e.g. _http, ws, https, wss, blob:https_\n\n### API Methods\n\n### `URISanity.vet(uri: String [, options: Object]): String`\n\u003eThe `.vet(uri: String [, options: Object])` method is used to sanitize a URI of any [standard form](https://en.wikipedia.org/wiki/List_of_URI_schemes) to ensure that it doesn't contain unwanted and/or malicious content. Only the second argument is optional. If the second ( **options** ) argument isn't passed, it means that all [flag options](https://github.com/codesplinta/URISanity#flag-options) are `false`.\n\n### `URISanity.extractParamValueFromUri(uri: String, queryParamName: String): String`\n\u003eThe `.extractParamValueFromUri(uri: String, queryParamName: String)` method is used to extract the value of a query parameter from a given URI. Both arguments are not optional.\n\n### `URISanity.checkParamsOverWhiteList(uri: String, paramNamesWhiteList: Array [, querySearch: String]): Boolean`\n\u003eThe `.checkParamsOverWhiteList(uri: String, queryParamNames: Array [, querySearch: String | Object])` method is used to check whether the params (query OR body) associated with a given URI is correct, allowed and valid for it's use case. Only the third argument for this method is optional.\n\n### `URISanity.isSameOrigin(uri: String): Boolean`\n\u003eThe `.isSameOrigin(uri: String)` method is used to check if the URI being inspected has the sam origin (protocol + host) as the environment (Browser or NodeJS). The only argument for this method is not optional.\n\n## License\n\nMIT License\n\n## Contributing\n\nIf you wish to contribute to this project, you are very much welcome. Please, create an issue first before you proceed to create a PR (either to propose a feature or fix a bug). Make sure to clone the repo, checkout to a contribution branch and build the project before making modifications to the codebase.\n\nRun all the following command (in order they appear) below:\n\n```bash\n\n$ npm run lint\n\n$ npm run build\n\n$ npm run test\n```\n\n## TypeScript\n\nYou can find the TS declaration [here](https://gist.github.com/isocroft/021098c660318bf92f7ab05bba042f48) (simply copy from the gist and paste in the root of your project as `urisanity.d.ts`)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodesplinta%2Furisanity","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodesplinta%2Furisanity","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodesplinta%2Furisanity/lists"}