/** * @name Inclusion of functionality from an untrusted source * @description Including functionality from an untrusted source may allow * an attacker to control the functionality and execute arbitrary code. * @kind problem * @problem.severity warning * @security-severity 6.0 * @precision high * @id js/functionality-from-untrusted-source * @tags security * external/cwe/cwe-830 */ import javascript /** A location that adds a reference to an untrusted source. */ abstract class AddsUntrustedUrl extends Locatable { /** Gets an explanation why this source is untrusted. */ abstract string getProblem(); } module StaticCreation { /** Holds if `host` is an alias of localhost. */ bindingset[host] predicate isLocalhostPrefix(string host) { host.toLowerCase() .regexpMatch([ "(?i)localhost(:[0-9]+)?/.*", "127.0.0.1(:[0-9]+)?/.*", "::1/.*", "\\[::1\\]:[0-9]+/.*" ]) } /** Holds if `url` is a url that is vulnerable to a MITM attack. */ bindingset[url] predicate isUntrustedSourceUrl(string url) { exists(string hostPath | hostPath = url.regexpCapture("(?i)http://(.*)", 1) | not isLocalhostPrefix(hostPath) ) } /** Holds if `url` refers to a CDN that needs an integrity check - even with https. */ bindingset[url] predicate isCdnUrlWithCheckingRequired(string url) { // Some CDN URLs are required to have an integrity attribute. We only add CDNs to that list // that recommend integrity-checking. url.regexpMatch("(?i)^https?://" + [ "code\\.jquery\\.com", // "cdnjs\\.cloudflare\\.com", // "cdnjs\\.com" // ] + "/.*\\.js$") } /** A script element that refers to untrusted content. */ class ScriptElementWithUntrustedContent extends AddsUntrustedUrl instanceof HTML::ScriptElement { ScriptElementWithUntrustedContent() { not exists(string digest | not digest = "" | super.getIntegrityDigest() = digest) and isUntrustedSourceUrl(super.getSourcePath()) } override string getProblem() { result = "Script loaded using unencrypted connection." } } /** A script element that refers to untrusted content. */ class CDNScriptElementWithUntrustedContent extends AddsUntrustedUrl, HTML::ScriptElement { CDNScriptElementWithUntrustedContent() { not exists(string digest | not digest = "" | this.getIntegrityDigest() = digest) and isCdnUrlWithCheckingRequired(this.getSourcePath()) } override string getProblem() { result = "Script loaded from content delivery network with no integrity check." } } /** An iframe element that includes untrusted content. */ class IframeElementWithUntrustedContent extends AddsUntrustedUrl instanceof HTML::IframeElement { IframeElementWithUntrustedContent() { isUntrustedSourceUrl(super.getSourcePath()) } override string getProblem() { result = "Iframe loaded using unencrypted connection." } } } module DynamicCreation { /** Holds if `call` creates a tag of kind `name`. */ predicate isCreateElementNode(DataFlow::CallNode call, string name) { call = DataFlow::globalVarRef("document").getAMethodCall("createElement") and call.getArgument(0).getStringValue().toLowerCase() = name } DataFlow::Node getAttributeAssignmentRhs(DataFlow::CallNode createCall, string name) { result = createCall.getAPropertyWrite(name).getRhs() or exists(DataFlow::InvokeNode inv | inv = createCall.getAMemberInvocation("setAttribute") | inv.getArgument(0).getStringValue() = name and result = inv.getArgument(1) ) } /** * Holds if `createCall` creates a `