-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[pull] main from github:main #2
base: main
Are you sure you want to change the base?
Conversation
QHelp previews: cpp/ql/src/experimental/Security/CWE/CWE-805/BufferAccessWithIncorrectLengthValue.qhelpBuffer access with incorrect length valueUsing a size argument that is larger than the buffer size will result in an out-of-bounds memory access and possibly overflow. You need to limit the value of the length argument. ExampleThe following example shows the use of a function with and without an error in the size argument. ...
char buf[256];
X509_NAME_oneline(X509_get_subject_name(peer),buf,sizeof(buf)); // GOOD
...
char buf[256];
X509_NAME_oneline(X509_get_subject_name(peer),buf,1024); // BAD
...
References
go/ql/src/InconsistentCode/UnhandledCloseWritableHandle.qhelpWritable file handle closed without error handlingData written to a file handle may not immediately be flushed to the underlying storage medium by the operating system. Often, the data may be cached in memory until the handle is closed, or until a later point after that. Only calling RecommendationAlways check whether ExampleIn this first example, two calls to package main
import (
"os"
)
func example() error {
f, err := os.OpenFile("file.txt", os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
return err
}
if _, err := f.WriteString("Hello"); err != nil {
f.Close()
return err
}
f.Close()
return nil
} In the second example, a call to package main
import (
"os"
)
func example() error {
f, err := os.OpenFile("file.txt", os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
return err
}
defer f.Close()
if _, err := f.WriteString("Hello"); err != nil {
return err
}
return nil
} To correct this issue, handle errors arising from calls to package main
import (
"os"
)
func example() (exampleError error) {
f, err := os.OpenFile("file.txt", os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
return err
}
defer func() {
// try to close the file; if an error occurs, set the error returned by `example`
// to that error, but only if `WriteString` didn't already set it to something first
if err := f.Close(); exampleError == nil && err != nil {
exampleError = err
}
}()
if _, err := f.WriteString("Hello"); err != nil {
exampleError = err
}
return
} References
go/ql/src/Security/CWE-601/BadRedirectCheck.qhelpBad redirect checkRedirect URLs should be checked to ensure that user input cannot cause a site to redirect to arbitrary domains. This is often done with a check that the redirect URL begins with a slash, which most of the time is an absolute redirect on the same host. However, browsers interpret URLs beginning with RecommendationAlso disallow redirect URLs starting with ExampleThe following function validates a (presumably untrusted) redirect URL package main
func sanitizeUrl(redir string) string {
if len(redir) > 0 && redir[0] == '/' {
return redir
}
return "/"
} While this check provides partial protection, it should be extended to cover package main
func sanitizeUrl1(redir string) string {
if len(redir) > 1 && redir[0] == '/' && redir[1] != '/' && redir[1] != '\\' {
return redir
}
return "/"
} References
java/ql/src/Security/CWE/CWE-023/PartialPathTraversal.qhelpPartial path traversal vulnerabilityA common way to check that a user-supplied path See also RecommendationIf the user should only access items within a certain directory ExampleIn this example, the public class PartialPathTraversalBad {
public void example(File dir, File parent) throws IOException {
if (!dir.getCanonicalPath().startsWith(parent.getCanonicalPath())) {
throw new IOException("Invalid directory: " + dir.getCanonicalPath());
}
}
} In this example, the public class PartialPathTraversalGood {
public void example(File dir, File parent) throws IOException {
if (!dir.getCanonicalPath().startsWith(parent.getCanonicalPath() + File.separator)) {
throw new IOException("Invalid directory: " + dir.getCanonicalPath());
}
}
} References
java/ql/src/Security/CWE/CWE-023/PartialPathTraversalFromRemote.qhelpPartial path traversal vulnerability from remoteA common way to check that a user-supplied path See also RecommendationIf the user should only access items within a certain directory ExampleIn this example, the public class PartialPathTraversalBad {
public void example(File dir, File parent) throws IOException {
if (!dir.getCanonicalPath().startsWith(parent.getCanonicalPath())) {
throw new IOException("Invalid directory: " + dir.getCanonicalPath());
}
}
} In this example, the public class PartialPathTraversalGood {
public void example(File dir, File parent) throws IOException {
if (!dir.getCanonicalPath().startsWith(parent.getCanonicalPath() + File.separator)) {
throw new IOException("Invalid directory: " + dir.getCanonicalPath());
}
}
} References
java/ql/src/Security/CWE/CWE-079/AndroidWebViewAddJavascriptInterface.qhelpAccess Java object methods through JavaScript exposureCalling the Objects exposed to JavaScript are available in all frames of the WebView. RecommendationIf you need to expose Java objects to JavaScript, guarantee that no untrusted third-party content is loaded into the WebView. ExampleIn the following (bad) example, a Java object is exposed to JavaScript. import android.webkit.JavascriptInterface;
import android.database.sqlite.SQLiteOpenHelper;
class ExposedObject extends SQLiteOpenHelper {
@JavascriptInterface
public String studentEmail(String studentName) {
// SQL injection
String query = "SELECT email FROM students WHERE studentname = '" + studentName + "'";
Cursor cursor = db.rawQuery(query, null);
cursor.moveToFirst();
String email = cursor.getString(0);
return email;
}
}
webview.getSettings().setJavaScriptEnabled(true);
webview.addJavaScriptInterface(new ExposedObject(), "exposedObject");
webview.loadData("", "text/html", null);
String name = "Robert'; DROP TABLE students; --";
webview.loadUrl("javascript:alert(exposedObject.studentEmail(\""+ name +"\"))"); References
java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.qhelpAndroid WebView JavaScript settingsEnabling JavaScript in an Android WebView allows the execution of JavaScript code in the context of the running application. This creates a cross-site scripting vulnerability. For example, if your application's WebView allows for visiting web pages that you do not trust, it is possible for an attacker to lead the user to a page which loads malicious JavaScript. You can enable or disable Javascript execution using the RecommendationJavaScript execution is disabled by default. You can explicitly disable it by calling If JavaScript is necessary, only load content from trusted servers using encrypted channels, such as HTTPS with certificate verification. ExampleIn the following (bad) example, a WebView has JavaScript enabled in its settings: WebSettings settings = webview.getSettings();
settings.setJavaScriptEnabled(true); In the following (good) example, a WebView explicitly disallows JavaScript execution: WebSettings settings = webview.getSettings();
settings.setJavaScriptEnabled(false); References
java/ql/src/Security/CWE/CWE-089/SqlConcatenated.qhelpQuery built by concatenation with a possibly-untrusted stringEven when the components of a SQL query are not fully controlled by a user, it is a vulnerability to build the query by directly concatenating those components. Perhaps a separate vulnerability will allow the user to gain control of the component. As well, a user who cannot gain full control of an input might influence it enough to cause the SQL query to fail to run. RecommendationUsually, it is better to use a SQL prepared statement than to build a complete SQL query with string concatenation. A prepared statement can include a wildcard, written as a question mark (?), for each part of the SQL query that is expected to be filled in by a different value each time it is run. When the query is later executed, a value must be supplied for each wildcard in the query. In the Java Persistence Query Language, it is better to use queries with parameters than to build a complete query with string concatenation. A Java Persistence query can include a parameter placeholder for each part of the query that is expected to be filled in by a different value when run. A parameter placeholder may be indicated by a colon (:) followed by a parameter name, or by a question mark (?) followed by an integer position. When the query is later executed, a value must be supplied for each parameter in the query, using the It is good practice to use prepared statements (in SQL) or query parameters (in the Java Persistence Query Language) for supplying parameter values to a query, whether or not any of the parameters are directly traceable to user input. Doing so avoids any need to worry about quoting and escaping. ExampleIn the following example, the code runs a simple SQL query in two different ways. The first way involves building a query, The second way, which shows good practice, involves building a query, {
// BAD: the category might have SQL special characters in it
String category = getCategory();
Statement statement = connection.createStatement();
String query1 = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ category + "' ORDER BY PRICE";
ResultSet results = statement.executeQuery(query1);
}
{
// GOOD: use a prepared query
String category = getCategory();
String query2 = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY=? ORDER BY PRICE";
PreparedStatement statement = connection.prepareStatement(query2);
statement.setString(1, category);
ResultSet results = statement.executeQuery();
} References
java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsAllowsContentAccess.qhelpAndroid WebView settings allows access to content linksAndroid can provide access to content providers within a WebView using the Allowing access to content providers via RecommendationIf your app does not require access to the ExampleIn the following (bad) example, access to WebSettings settings = webview.getSettings();
settings.setAllowContentAccess(true); In the following (good) example, access to WebSettings settings = webview.getSettings();
settings.setAllowContentAccess(false); References
java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.qhelpAndroid WebSettings file accessAllowing file access in an Android WebView can expose a device's file system to the JavaScript running in that WebView. If the JavaScript contains vulnerabilities or the WebView loads untrusted content, file access allows an attacker to steal the user's data. RecommendationWhen possible, do not allow file access. The file access settings are disabled by default. You can explicitly disable file access by setting the following settings to
ExampleIn the following (bad) example, the WebView is configured with settings that allow local file access. WebSettings settings = view.getSettings();
settings.setAllowFileAccess(true);
settings.setAllowFileAccessFromURLs(true);
settings.setAllowUniversalAccessFromURLs(true); In the following (good) example, the WebView is configured to disallow file access. WebSettings settings = view.getSettings();
settings.setAllowFileAccess(false);
settings.setAllowFileAccessFromURLs(false);
settings.setAllowUniversalAccessFromURLs(false); As mentioned previously, asset loaders can load files without file system access. In the following (good) example, an asset loader is configured to load assets over HTTPS. WebViewAssetLoader loader = new WebViewAssetLoader.Builder()
// Replace the domain with a domain you control, or use the default
// appassets.androidplatform.com
.setDomain("appassets.example.com")
.addPathHandler("/resources", new AssetsPathHandler(this))
.build();
webView.setWebViewClient(new WebViewClientCompat() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
return assetLoader.shouldInterceptRequest(request.getUrl());
}
});
webView.loadUrl("https://appassets.example.com/resources/www/index.html"); References
java/ql/src/Security/CWE/CWE-295/AndroidMissingCertificatePinning.qhelpAndroid missing certificate pinningCertificate pinning is the practice of only trusting a specific set of SSL certificates, rather than those that the device trusts by default. In Android applications, it is reccomended to use certificate pinning when communicating over the network, in order to minimize the risk of machine-in-the-middle attacks from a compromised CA. RecommendationThe easiest way to implement certificate pinning is to declare your pins in a Another way to implement certificate pinning is to use the `CertificatePinner` class from the `okhttp` library. A final way to implement certificate pinning is to use a ExampleIn the first (bad) case below, a network call is performed with no certificate pinning implemented. The other (good) cases demonstrate the different ways to implement certificate pinning. // BAD - By default, this network call does not use certificate pinning
URLConnection conn = new URL("https://example.com").openConnection(); <!-- GOOD: Certificate pinning implemented via a Network Security Config file -->
<!-- In AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<application android:networkSecurityConfig="@xml/NetworkSecurityConfig">
...
</application>
</manifest>
<!-- In res/xml/NetworkSecurityConfig.xml -->
<network-security-config>
<domain-config>
<domain>good.example.com</domain>
<pin-set expiration="2038/1/19">
<pin digest="SHA-256">...</pin>
</pin-set>
</domain-config>
</network-security-config> // GOOD: Certificate pinning implemented via okhttp3.CertificatePinner
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
OkHttpClient client = new OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build();
client.newCall(new Request.Builder().url("https://example.com").build()).execute();
// GOOD: Certificate pinning implemented via a TrustManager
KeyStore keyStore = KeyStore.getInstance("BKS");
keyStore.load(resources.openRawResource(R.raw.cert), null);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
URL url = new URL("http://www.example.com/");
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setSSLSocketFactory(sslContext.getSocketFactory()); References
java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.qhelpDeserialization of user-controlled dataDeserializing untrusted data using any deserialization framework that allows the construction of arbitrary serializable objects is easily exploitable and in many cases allows an attacker to execute arbitrary code. Even before a deserialized object is returned to the caller of a deserialization method a lot of code may have been executed, including static initializers, constructors, and finalizers. Automatic deserialization of fields means that an attacker may craft a nested combination of objects on which the executed initialization code may have unforeseen effects, such as the execution of arbitrary code. There are many different serialization frameworks. This query currently supports Kryo, XmlDecoder, XStream, SnakeYaml, JYaml, JsonIO, YAMLBeans, HessianBurlap, Castor, Burlap, Jackson, Jabsorb, Jodd JSON, Flexjson, Gson and Java IO serialization through RecommendationAvoid deserialization of untrusted data if at all possible. If the architecture permits it then use other formats instead of serialized objects, for example JSON or XML. However, these formats should not be deserialized into complex objects because this provides further opportunities for attack. For example, XML-based deserialization attacks are possible through libraries such as XStream and XmlDecoder. Alternatively, a tightly controlled whitelist can limit the vulnerability of code, but be aware of the existence of so-called Bypass Gadgets, which can circumvent such protection measures. Recommendations specific to particular frameworks supported by this query: FastJson -
FasterXML -
Kryo -
ObjectInputStream -
SnakeYAML -
XML Decoder -
ExampleThe following example calls public MyObject {
public int field;
MyObject(int field) {
this.field = field;
}
}
public MyObject deserialize(Socket sock) {
try(ObjectInputStream in = new ObjectInputStream(sock.getInputStream())) {
return (MyObject)in.readObject(); // unsafe
}
} Rewriting the communication protocol to only rely on reading primitive types from the input stream removes the vulnerability. public MyObject deserialize(Socket sock) {
try(DataInputStream in = new DataInputStream(sock.getInputStream())) {
return new MyObject(in.readInt());
}
} References
java/ql/src/Security/CWE/CWE-611/XXELocal.qhelpResolving XML external entity in user-controlled data from local sourceParsing untrusted XML files with a weakly configured XML parser may lead to an XML External Entity (XXE) attack. This type of attack uses external entity references to access arbitrary files on a system, carry out denial of service, or server side request forgery. Even when the result of parsing is not returned to the user, out-of-band data retrieval techniques may allow attackers to steal sensitive data. Denial of services can also be carried out in this situation. There are many XML parsers for Java, and most of them are vulnerable to XXE because their default settings enable parsing of external entities. This query currently identifies vulnerable XML parsing from the following parsers: RecommendationThe best way to prevent XXE attacks is to disable the parsing of any Document Type Declarations (DTDs) in untrusted data. If this is not possible you should disable the parsing of external general entities and external parameter entities. This improves security but the code will still be at risk of denial of service and server side request forgery attacks. Protection against denial of service attacks may also be implemented by setting entity expansion limits, which is done by default in recent JDK and JRE implementations. Because there are many different ways to disable external entity retrieval with varying support between different providers, in this query we choose to specifically check for the OWASP recommended way to disable external entity retrieval for a particular parser. There may be other ways of making a particular parser safe which deviate from these guidelines, in which case this query will continue to flag the parser as potentially dangerous. ExampleThe following example calls public void parse(Socket sock) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
builder.parse(sock.getInputStream()); //unsafe
} In this example, the public void disableDTDParse(Socket sock) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
DocumentBuilder builder = factory.newDocumentBuilder();
builder.parse(sock.getInputStream()); //safe
} References
java/ql/src/Security/CWE/CWE-730/PolynomialReDoS.qhelpPolynomial regular expression used on uncontrolled dataSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length n is proportional to nk or even 2n. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service ("DoS") attack by crafting an expensive input string for the regular expression to match. The regular expression engine provided by Java uses a backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower. Typically, a regular expression is affected by this problem if it contains a repetition of the form Note that Java versions 9 and above have some mitigations against ReDoS; however they aren't perfect and more complex regular expressions can still be affected by this problem. RecommendationModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter. Alternatively, an alternate regex library that guarantees linear time execution, such as Google's RE2J, may be used. ExampleConsider this use of a regular expression, which removes all leading and trailing whitespace in a string: Pattern.compile("^\\s+|\\s+$").matcher(text).replaceAll("") // BAD
The sub-expression This ultimately means that the time cost of trimming a string is quadratic in the length of the string. So a string like Avoid this problem by rewriting the regular expression to not contain the ambiguity about when to start matching whitespace sequences. For instance, by using a negative look-behind ( Note that the sub-expression ExampleAs a similar, but slightly subtler problem, consider the regular expression that matches lines with numbers, possibly written using scientific notation: "^0\\.\\d+E?\\d+$""
The problem with this regular expression is in the sub-expression This is problematic for strings that do not end with a digit. Such a string will force the regular expression engine to process each digit sequence once per digit in the sequence, again leading to a quadratic time complexity. To make the processing faster, the regular expression should be rewritten such that the two References
java/ql/src/Security/CWE/CWE-730/ReDoS.qhelpInefficient regular expressionSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length n is proportional to nk or even 2n. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service ("DoS") attack by crafting an expensive input string for the regular expression to match. The regular expression engine provided by Java uses a backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower. Typically, a regular expression is affected by this problem if it contains a repetition of the form Note that Java versions 9 and above have some mitigations against ReDoS; however they aren't perfect and more complex regular expressions can still be affected by this problem. RecommendationModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter. Alternatively, an alternate regex library that guarantees linear time execution, such as Google's RE2J, may be used. ExampleConsider this regular expression: ^_(__|.)+_$
Its sub-expression This problem can be avoided by rewriting the regular expression to remove the ambiguity between the two branches of the alternative inside the repetition: ^_(__|[^_])+_$
References
java/ql/src/Security/CWE/CWE-927/SensitiveResultReceiver.qhelpLeaking sensitive information through a ResultReceiverIf a RecommendationDo not send sensitive data to an untrusted ExampleIn the following (bad) example, sensitive data is sent to an untrusted // BAD: Sensitive data is sent to an untrusted result receiver
void bad(String password) {
Intent intent = getIntent();
ResultReceiver rec = intent.getParcelableExtra("Receiver");
Bundle b = new Bundle();
b.putCharSequence("pass", password);
rec.send(0, b);
} References
java/ql/src/experimental/Security/CWE/CWE-400/LocalThreadResourceAbuse.qhelpUncontrolled thread resource consumption from local input sourceThe RecommendationTo guard against this attack, consider specifying an upper range of allowed sleep time or adopting the producer/consumer design pattern with ExampleThe following example shows a bad situation and a good situation respectively. In the bad situation, a thread sleep time comes directly from user input. In the good situation, an upper range check on the maximum sleep time allowed is enforced. class SleepTest {
public void test(int userSuppliedWaitTime) throws Exception {
// BAD: no boundary check on wait time
Thread.sleep(userSuppliedWaitTime);
// GOOD: enforce an upper limit on wait time
if (userSuppliedWaitTime > 0 && userSuppliedWaitTime < 5000) {
Thread.sleep(userSuppliedWaitTime);
}
}
} References
javascript/ql/src/Performance/PolynomialReDoS.qhelpPolynomial regular expression used on uncontrolled dataSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length n is proportional to nk or even 2n. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service ("DoS") attack by crafting an expensive input string for the regular expression to match. The regular expression engines provided by many popular JavaScript platforms use backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower. Typically, a regular expression is affected by this problem if it contains a repetition of the form RecommendationModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter. ExampleConsider this use of a regular expression, which removes all leading and trailing whitespace in a string: text.replace(/^\s+|\s+$/g, ''); // BAD
The sub-expression This ultimately means that the time cost of trimming a string is quadratic in the length of the string. So a string like Avoid this problem by rewriting the regular expression to not contain the ambiguity about when to start matching whitespace sequences. For instance, by using a negative look-behind ( Note that the sub-expression ExampleAs a similar, but slightly subtler problem, consider the regular expression that matches lines with numbers, possibly written using scientific notation: /^0\.\d+E?\d+$/.test(str) // BAD
The problem with this regular expression is in the sub-expression This is problematic for strings that do not end with a digit. Such a string will force the regular expression engine to process each digit sequence once per digit in the sequence, again leading to a quadratic time complexity. To make the processing faster, the regular expression should be rewritten such that the two References
javascript/ql/src/Performance/ReDoS.qhelpInefficient regular expressionSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length n is proportional to nk or even 2n. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service ("DoS") attack by crafting an expensive input string for the regular expression to match. The regular expression engines provided by many popular JavaScript platforms use backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower. Typically, a regular expression is affected by this problem if it contains a repetition of the form RecommendationModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter. ExampleConsider this regular expression: /^_(__|.)+_$/
Its sub-expression This problem can be avoided by rewriting the regular expression to remove the ambiguity between the two branches of the alternative inside the repetition: /^_(__|[^_])+_$/
References
javascript/ql/src/Security/CWE-020/UntrustedDataToExternalAPI.qhelpUntrusted data passed to external APIUsing unsanitized untrusted data in an external API can cause a variety of security issues. This query reports external APIs that use untrusted data. The results are not filtered so that you can audit all examples. The query provides data for security reviews of the application and you can also use it to identify external APIs that should be modeled as either taint steps, or sinks for specific problems. An external API is defined as a method call to a method that is not defined in the source code, not overridden in the source code, and is not modeled as a taint step in the default taint library. External APIs may be from the third-party dependencies or from internal dependencies. The query reports uses of untrusted data one of the arguments of external API call or in the return value from a callback passed to an external API. RecommendationFor each result:
ExampleIn this first example, a query parameter is read from the express().get('/news', (req, res) => {
let topic = req.query.topic;
res.send(`<h1>${topic}</h1>`);
}); This is a reflected XSS sink. The XSS query should therefore be reviewed to confirm that this sink is appropriately modeled, and if it is, to confirm that the query reports this particular result, or that the result is a false positive due to some existing sanitization. In this second example, again a query parameter is read from let path = require('path');
express().get('/data', (req, res) => {
let file = path.join(HOME_DIR, 'public', req.query.file);
res.sendFile(file);
}); If the query reported the call to Note that both examples are correctly handled by the standard taint tracking library and security queries. References
javascript/ql/src/Security/CWE-078/CommandInjection.qhelpUncontrolled command lineCode that passes user input directly to RecommendationIf possible, use hard-coded string literals to specify the command to run or library to load. Instead of passing the user input directly to the process or library function, examine the user input and then choose among hard-coded string literals. If the applicable libraries or commands cannot be determined at compile time, then add code to verify that the user input string is safe before using it. ExampleThe following example shows code that takes a shell script that can be changed maliciously by a user, and passes it straight to var cp = require("child_process"),
http = require('http'),
url = require('url');
var server = http.createServer(function(req, res) {
let cmd = url.parse(req.url, true).query.path;
cp.exec(cmd); // BAD
}); References
javascript/ql/src/Security/CWE-079/Xss.qhelpClient-side cross-site scriptingDirectly writing user input (for example, a URL query parameter) to a webpage without properly sanitizing the input first, allows for a cross-site scripting vulnerability. This kind of vulnerability is also called DOM-based cross-site scripting, to distinguish it from other types of cross-site scripting. RecommendationTo guard against cross-site scripting, consider using contextual output encoding/escaping before writing user input to the page, or one of the other solutions that are mentioned in the references. ExampleThe following example shows part of the page URL being written directly to the document, leaving the website vulnerable to cross-site scripting. function setLanguageOptions() {
var href = document.location.href,
deflt = href.substring(href.indexOf("default=")+8);
document.write("<OPTION value=1>"+deflt+"</OPTION>");
document.write("<OPTION value=2>English</OPTION>");
} References
javascript/ql/src/Security/CWE-089/SqlInjection.qhelpDatabase query built from user-controlled sourcesIf a database query (such as a SQL or NoSQL query) is built from user-provided data without sufficient sanitization, a malicious user may be able to run malicious database queries. RecommendationMost database connector libraries offer a way of safely embedding untrusted data into a query by means of query parameters or prepared statements. For NoSQL queries, make use of an operator like MongoDB's ExampleIn the following example, assume the function The handler constructs two copies of the same SQL query involving user input taken from the request object, once unsafely using string concatenation, and once safely using query parameters. In the first case, the query string In the second case, the parameter is embedded into the query string const app = require("express")(),
pg = require("pg"),
pool = new pg.Pool(config);
app.get("search", function handler(req, res) {
// BAD: the category might have SQL special characters in it
var query1 =
"SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" +
req.params.category +
"' ORDER BY PRICE";
pool.query(query1, [], function(err, results) {
// process results
});
// GOOD: use parameters
var query2 =
"SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY=$1" + " ORDER BY PRICE";
pool.query(query2, [req.params.category], function(err, results) {
// process results
});
}); References
javascript/ql/src/Security/CWE-094/CodeInjection.qhelpCode injectionDirectly evaluating user input (for example, an HTTP request parameter) as code without properly sanitizing the input first allows an attacker arbitrary code execution. This can occur when user input is treated as JavaScript, or passed to a framework which interprets it as an expression to be evaluated. Examples include AngularJS expressions or JQuery selectors. RecommendationAvoid including user input in any expression which may be dynamically evaluated. If user input must be included, use context-specific escaping before including it. It is important that the correct escaping is used for the type of evaluation that will occur. ExampleThe following example shows part of the page URL being evaluated as JavaScript code. This allows an attacker to provide JavaScript within the URL. If an attacker can persuade a user to click on a link to such a URL, the attacker can evaluate arbitrary JavaScript in the browser of the user to, for example, steal cookies containing session information. eval(document.location.href.substring(document.location.href.indexOf("default=")+8)) The following example shows a Pug template being constructed from user input, allowing attackers to run arbitrary code via a payload such as const express = require('express')
var pug = require('pug');
const app = express()
app.post('/', (req, res) => {
var input = req.query.username;
var template = `
doctype
html
head
title= 'Hello world'
body
form(action='/' method='post')
input#name.form-control(type='text)
button.btn.btn-primary(type='submit') Submit
p Hello `+ input
var fn = pug.compile(template);
var html = fn();
res.send(html);
}) Below is an example of how to use a template engine without any risk of template injection. The user input is included via an interpolation expression const express = require('express')
var pug = require('pug');
const app = express()
app.post('/', (req, res) => {
var input = req.query.username;
var template = `
doctype
html
head
title= 'Hello world'
body
form(action='/' method='post')
input#name.form-control(type='text)
button.btn.btn-primary(type='submit') Submit
p Hello #{username}`
var fn = pug.compile(template);
var html = fn({username: input});
res.send(html);
}) References
javascript/ql/src/Security/CWE-117/LogInjection.qhelpLog injectionIf unsanitized user input is written to a log entry, a malicious user may be able to forge new log entries. Forgery can occur if a user provides some input with characters that are interpreted when the log output is displayed. If the log is displayed as a plain text file, then new line characters can be used by a malicious user. If the log is displayed as HTML, then arbitrary HTML may be included to spoof log entries. RecommendationUser input should be suitably sanitized before it is logged. If the log entries are in plain text then line breaks should be removed from user input, using For log entries that will be displayed in HTML, user input should be HTML-encoded before being logged, to prevent forgery and other forms of HTML injection. ExampleIn the first example, a username, provided by the user, is logged using `console.info`. In the first case, it is logged without any sanitization. In the second case, the username is used to build an error that is logged using `console.error`. If a malicious user provides `username=Guest%0a[INFO]+User:+Admin%0a` as a username parameter, the log entry will be splitted in two different lines, where the second line will be `[INFO]+User:+Admin`. const http = require('http');
const url = require('url');
const server = http.createServer((req, res) => {
let q = url.parse(req.url, true);
console.info(`[INFO] User: ${q.query.username}`); // BAD: User input logged as-is
})
server.listen(3000, '127.0.0.1', () => {}); In the second example, const http = require('http');
const url = require('url');
const server = http.createServer((req, res) => {
let q = url.parse(req.url, true);
// GOOD: remove newlines from user controlled input before logging
let username = q.query.username.replace(/\n|\r/g, "");
console.info(`[INFO] User: ${username}`);
});
server.listen(3000, '127.0.0.1', () => {}); References
javascript/ql/src/Security/CWE-134/TaintedFormatString.qhelpUse of externally-controlled format stringFunctions like the Node.js standard library function RecommendationEither sanitize the input before including it in the format string, or use a ExampleThe following program snippet logs information about an unauthorized access attempt. The log message includes the user name, and the user's IP address is passed as an additional argument to const app = require("express")();
app.get("unauthorized", function handler(req, res) {
let user = req.query.user;
let ip = req.connection.remoteAddress;
console.log("Unauthorized access attempt by " + user, ip);
}); However, if a malicious user provides Instead, the user name should be included using the const app = require("express")();
app.get("unauthorized", function handler(req, res) {
let user = req.query.user;
let ip = req.connection.remoteAddress;
console.log("Unauthorized access attempt by %s", user, ip);
}); References
javascript/ql/src/Security/CWE-312/CleartextLogging.qhelpClear-text logging of sensitive informationIf sensitive data is written to a log entry it could be exposed to an attacker who gains access to the logs. Potential attackers can obtain sensitive user data when the log output is displayed. Additionally that data may expose system information such as full path names, system information, and sometimes usernames and passwords. RecommendationSensitive data should not be logged. ExampleIn the example the entire process environment is logged using `console.info`. Regular users of the production deployed application should not have access to this much information about the environment configuration. // BAD: Logging cleartext sensitive data
console.info(`[INFO] Environment: ${process.env}`); In the second example the data that is logged is not sensitive. let not_sensitive_data = { a: 1, b : 2}
// GOOD: it is fine to log data that is not sensitive
console.info(`[INFO] Some object contains: ${not_sensitive_data}`); References
javascript/ql/src/Security/CWE-346/CorsMisconfigurationForCredentials.qhelpCORS misconfiguration for credentials transferA server can send the When the RecommendationWhen the Since the ExampleIn the example below, the server allows the browser to send user credentials in a Cross-Origin request. The request header var https = require('https'),
url = require('url');
var server = https.createServer(function(){});
server.on('request', function(req, res) {
let origin = url.parse(req.url, true).query.origin;
// BAD: attacker can choose the value of origin
res.setHeader("Access-Control-Allow-Origin", origin);
res.setHeader("Access-Control-Allow-Credentials", true);
// ...
}); This is not secure, since an attacker can choose the value of the var https = require('https'),
url = require('url');
var server = https.createServer(function(){});
server.on('request', function(req, res) {
let origin = url.parse(req.url, true).query.origin,
whitelist = {
"https://example.com": true,
"https://subdomain.example.com": true,
"https://example.com:1337": true
};
if (origin in whitelist) {
// GOOD: the origin is in the whitelist
res.setHeader("Access-Control-Allow-Origin", origin);
res.setHeader("Access-Control-Allow-Credentials", true);
}
// ...
}); References
javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.qhelpMissing CSRF middlewareWebsites that rely on cookie-based authentication may be vulnerable to cross-site request forgery (CSRF). Specifically, a state-changing request should include a secret token so the request can't be forged by an attacker. Otherwise, unwanted requests can be submitted on behalf of a user who visits a malicious website. This is typically mitigated by embedding a session-specific secret token in each request. This token is then checked as an additional authentication measure. A malicious website should have no way of guessing the correct token to embed in the request. RecommendationUse a middleware package such as ExampleIn the example below, the server authenticates users before performing the const app = require("express")(),
cookieParser = require("cookie-parser"),
bodyParser = require("body-parser"),
session = require("express-session");
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({ secret: process.env['SECRET'], cookie: { maxAge: 60000 } }));
// ...
app.post("/changeEmail", function(req, res) {
const userId = req.session.id;
const email = req.body["email"];
// ... update email associated with userId
}); This is not secure. An attacker can submit a POST This vulnerability can be mitigated by installing a CSRF protecting middleware handler: const app = require("express")(),
cookieParser = require("cookie-parser"),
bodyParser = require("body-parser"),
session = require("express-session"),
csrf = require('lusca').csrf;
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({ secret: process.env['SECRET'], cookie: { maxAge: 60000 } }));
app.use(csrf());
// ...
app.post("/changeEmail", function(req, res) {
const userId = req.session.id;
const email = req.body["email"];
// ... update email associated with userId
}); References
javascript/ql/src/Security/CWE-400/RemotePropertyInjection.qhelpRemote property injectionDynamically computing object property names from untrusted input may have multiple undesired consequences. For example, if the property access is used as part of a write, an attacker may overwrite vital properties of objects, such as Moreover, if the name of an HTTP header is user-controlled, an attacker may exploit this to overwrite security-critical headers such as RecommendationThe most common case in which prototype pollution vulnerabilities arise is when JavaScript objects are used for implementing map data structures. This case should be avoided whenever possible by using the ECMAScript 2015 When using user input as part of a header name, a sanitization step should be performed on the input to ensure that the name does not clash with existing header names such as ExampleIn the example below, the dynamically computed property var express = require('express');
var app = express();
var myObj = {}
app.get('/user/:id', function(req, res) {
var prop = req.query.userControlled; // BAD
myObj[prop] = function() {};
console.log("Request object " + myObj);
}); This is not secure since an attacker may exploit this code to overwrite the property var express = require('express');
var app = express();
var myObj = {}
app.get('/user/:id', function(req, res) {
var prop = "$" + req.query.userControlled; // GOOD
myObj[prop] = function() {};
console.log("Request object " + myObj);
}); References
javascript/ql/src/Security/CWE-502/UnsafeDeserialization.qhelpDeserialization of user-controlled dataDeserializing untrusted data using any deserialization framework that allows the construction of arbitrary functions is easily exploitable and, in many cases, allows an attacker to execute arbitrary code. RecommendationAvoid deserialization of untrusted data if at all possible. If the architecture permits it, then use formats like JSON or XML that cannot represent functions. When using YAML or other formats that support the serialization and deserialization of functions, ensure that the parser is configured to disable deserialization of arbitrary functions. ExampleThe following example calls the const app = require("express")(),
jsyaml = require("js-yaml");
app.get("load", function(req, res) {
let data = jsyaml.load(req.params.data);
// ...
}); Using the const app = require("express")(),
jsyaml = require("js-yaml");
app.get("load", function(req, res) {
let data = jsyaml.safeLoad(req.params.data);
// ...
}); References
javascript/ql/src/Security/CWE-611/Xxe.qhelpXML external entity expansionParsing untrusted XML files with a weakly configured XML parser may lead to an XML External Entity (XXE) attack. This type of attack uses external entity references to access arbitrary files on a system, carry out denial-of-service (DoS) attacks, or server-side request forgery. Even when the result of parsing is not returned to the user, DoS attacks are still possible and out-of-band data retrieval techniques may allow attackers to steal sensitive data. RecommendationThe easiest way to prevent XXE attacks is to disable external entity handling when parsing untrusted data. How this is done depends on the library being used. Note that some libraries, such as recent versions of ExampleThe following example uses the const app = require("express")(),
libxml = require("libxmljs");
app.post("upload", (req, res) => {
let xmlSrc = req.body,
doc = libxml.parseXml(xmlSrc, { noent: true });
}); To guard against XXE attacks, the const app = require("express")(),
libxml = require("libxmljs");
app.post("upload", (req, res) => {
let xmlSrc = req.body,
doc = libxml.parseXml(xmlSrc);
}); References
javascript/ql/src/Security/CWE-643/XpathInjection.qhelpXPath injectionIf an XPath expression is built using string concatenation, and the components of the concatenation include user input, it makes it very easy for a user to create a malicious XPath expression. RecommendationIf user input must be included in an XPath expression, either sanitize the data or use variable references to safely embed it without altering the structure of the expression. ExampleIn this example, the code accepts a user name specified by the user, and uses this unvalidated and unsanitized value in an XPath expression constructed using the const express = require('express');
const xpath = require('xpath');
const app = express();
app.get('/some/route', function(req, res) {
let userName = req.param("userName");
// BAD: Use user-provided data directly in an XPath expression
let badXPathExpr = xpath.parse("//users/user[login/text()='" + userName + "']/home_dir/text()");
badXPathExpr.select({
node: root
});
}); Instead, embed the user input using the variable replacement mechanism offered by const express = require('express');
const xpath = require('xpath');
const app = express();
app.get('/some/route', function(req, res) {
let userName = req.param("userName");
// GOOD: Embed user-provided data using variables
let goodXPathExpr = xpath.parse("//users/user[login/text()=$userName]/home_dir/text()");
goodXPathExpr.select({
node: root,
variables: { userName: userName }
});
}); References
javascript/ql/src/Security/CWE-730/RegExpInjection.qhelpRegular expression injectionConstructing a regular expression with unsanitized user input is dangerous as a malicious user may be able to modify the meaning of the expression. In particular, such a user may be able to provide a regular expression fragment that takes exponential time in the worst case, and use that to perform a Denial of Service attack. RecommendationBefore embedding user input into a regular expression, use a sanitization function such as lodash's ExampleThe following example shows a HTTP request parameter that is used to construct a regular expression without sanitizing it first: var express = require('express');
var app = express();
app.get('/findKey', function(req, res) {
var key = req.param("key"), input = req.param("input");
// BAD: Unsanitized user input is used to construct a regular expression
var re = new RegExp("\\b" + key + "=(.*)\n");
}); Instead, the request parameter should be sanitized first, for example using the function var express = require('express');
var _ = require('lodash');
var app = express();
app.get('/findKey', function(req, res) {
var key = req.param("key"), input = req.param("input");
// GOOD: User input is sanitized before constructing the regex
var safeKey = _.escapeRegExp(key);
var re = new RegExp("\\b" + safeKey + "=(.*)\n");
}); References
javascript/ql/src/Security/CWE-770/ResourceExhaustion.qhelpResource exhaustionApplications are constrained by how many resources they can make use of. Failing to respect these constraints may cause the application to be unresponsive or crash. It is therefore problematic if attackers can control the sizes or lifetimes of allocated objects. RecommendationEnsure that attackers can not control object sizes and their lifetimes. If object sizes and lifetimes must be controlled by external parties, ensure you restrict the object sizes and lifetimes so that they are within acceptable ranges. ExampleThe following example allocates a buffer with a user-controlled size. var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
var size = parseInt(url.parse(req.url, true).query.size);
let buffer = Buffer.alloc(size); // BAD
// ... use the buffer
}); This is problematic since an attacker can choose a size that makes the application run out of memory. Even worse, in older versions of Node.js, this could leak confidential memory. To prevent such attacks, limit the buffer size: var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
var size = parseInt(url.parse(req.url, true).query.size);
if (size > 1024) {
res.statusCode = 400;
res.end("Bad request.");
return;
}
let buffer = Buffer.alloc(size); // GOOD
// ... use the buffer
}); ExampleAs another example, consider an application that allocates an array with a user-controlled size, and then fills it with values: var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
var size = parseInt(url.parse(req.url, true).query.size);
let dogs = new Array(size).fill("dog"); // BAD
// ... use the dog
}); The allocation of the array itself is not problematic since arrays are allocated sparsely, but the subsequent filling of the array will take a long time, causing the application to be unresponsive, or even run out of memory. Again, a limit on the size will prevent the attack: var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
var size = parseInt(url.parse(req.url, true).query.size);
if (size > 1024) {
res.statusCode = 400;
res.end("Bad request.");
return;
}
let dogs = new Array(size).fill("dog"); // GOOD
// ... use the dogs
}); ExampleFinally, the following example lets a user choose a delay after which a function is executed: var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
var delay = parseInt(url.parse(req.url, true).query.delay);
setTimeout(f, delay); // BAD
}); This is problematic because a large delay essentially makes the application wait indefinitely before executing the function. Repeated registrations of such delays will therefore use up all of the memory in the application. A limit on the delay will prevent the attack: var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
var delay = parseInt(url.parse(req.url, true).query.delay);
if (delay > 1000) {
res.statusCode = 400;
res.end("Bad request.");
return;
}
setTimeout(f, delay); // GOOD
}); References
javascript/ql/src/Security/CWE-776/XmlBomb.qhelpXML internal entity expansionParsing untrusted XML files with a weakly configured XML parser may be vulnerable to denial-of-service (DoS) attacks exploiting uncontrolled internal entity expansion. In XML, so-called internal entities are a mechanism for introducing an abbreviation for a piece of text or part of a document. When a parser that has been configured to expand entities encounters a reference to an internal entity, it replaces the entity by the data it represents. The replacement text may itself contain other entity references, which are expanded recursively. This means that entity expansion can increase document size dramatically. If untrusted XML is parsed with entity expansion enabled, a malicious attacker could submit a document that contains very deeply nested entity definitions, causing the parser to take a very long time or use large amounts of memory. This is sometimes called an XML bomb attack. RecommendationThe safest way to prevent XML bomb attacks is to disable entity expansion when parsing untrusted data. How this is done depends on the library being used. Note that some libraries, such as recent versions of ExampleThe following example uses the XML parser provided by the const app = require("express")(),
expat = require("node-expat");
app.post("upload", (req, res) => {
let xmlSrc = req.body,
parser = new expat.Parser();
parser.on("startElement", handleStart);
parser.on("text", handleText);
parser.write(xmlSrc);
}); At the time of writing, const app = require("express")(),
sax = require("sax");
app.post("upload", (req, res) => {
let xmlSrc = req.body,
parser = sax.parser(true);
parser.onopentag = handleStart;
parser.ontext = handleText;
parser.write(xmlSrc);
}); References
javascript/ql/src/Security/CWE-807/ConditionalBypass.qhelpUser-controlled bypass of security checkUsing user-controlled data in a permissions check may allow a user to gain unauthorized access to protected functionality or data. RecommendationWhen checking whether a user is authorized for a particular activity, do not use data that is entirely controlled by that user in the permissions check. If necessary, always validate the input, ideally against a fixed list of expected values. Similarly, do not decide which permission to check for, based on user data. In particular, avoid using computation to decide which permissions to check for. Use fixed permissions for particular actions, rather than generating the permission to check for. ExampleIn this example, we have a server that shows private information for a user, based on the request parameter var express = require('express');
var app = express();
// ...
app.get('/full-profile/:userId', function(req, res) {
if (req.cookies.loggedInUserId !== req.params.userId) {
// BAD: login decision made based on user controlled data
requireLogin();
} else {
// ... show private information
}
}); This security check is, however, insufficient since an attacker can craft his cookie values to match those of any user. To prevent this, the server can cryptographically sign the security critical cookie values: var express = require('express');
var app = express();
// ...
app.get('/full-profile/:userId', function(req, res) {
if (req.signedCookies.loggedInUserId !== req.params.userId) {
// GOOD: login decision made based on server controlled data
requireLogin();
} else {
// ... show private information
}
}); Referencesjavascript/ql/src/Security/CWE-915/PrototypePollutingAssignment.qhelpPrototype-polluting assignmentMost JavaScript objects inherit the properties of the built-in One way to cause prototype pollution is by modifying an object obtained via a user-controlled property name. Most objects have a special RecommendationUse an associative data structure that is resilient to untrusted key values, such as a Map. In some cases, a prototype-less object created with Object.create(null) may be preferable. Alternatively, restrict the computed property name so it can't clash with a built-in property, either by prefixing it with a constant string, or by rejecting inputs that don't conform to the expected format. ExampleIn the example below, the untrusted value let express = require('express');
let app = express()
app.put('/todos/:id', (req, res) => {
let id = req.params.id;
let items = req.session.todos[id];
if (!items) {
items = req.session.todos[id] = {};
}
items[req.query.name] = req.query.text;
res.end(200);
}); One way to fix this is to use Map objects to associate key/value pairs instead of regular objects, as shown below: let express = require('express');
let app = express()
app.put('/todos/:id', (req, res) => {
let id = req.params.id;
let items = req.session.todos.get(id);
if (!items) {
items = new Map();
req.sessions.todos.set(id, items);
}
items.set(req.query.name, req.query.text);
res.end(200);
}); Referencesjavascript/ql/src/experimental/heuristics/ql/src/Security/CWE-020/UntrustedDataToExternalAPI.qhelpUntrusted data passed to external API with additional heuristic sourcesUsing unsanitized untrusted data in an external API can cause a variety of security issues. This query reports external APIs that use untrusted data. The results are not filtered so that you can audit all examples. The query provides data for security reviews of the application and you can also use it to identify external APIs that should be modeled as either taint steps, or sinks for specific problems. An external API is defined as a method call to a method that is not defined in the source code, not overridden in the source code, and is not modeled as a taint step in the default taint library. External APIs may be from the third-party dependencies or from internal dependencies. The query reports uses of untrusted data one of the arguments of external API call or in the return value from a callback passed to an external API. RecommendationFor each result:
ExampleIn this first example, a query parameter is read from the express().get('/news', (req, res) => {
let topic = req.query.topic;
res.send(`<h1>${topic}</h1>`);
}); This is a reflected XSS sink. The XSS query should therefore be reviewed to confirm that this sink is appropriately modeled, and if it is, to confirm that the query reports this particular result, or that the result is a false positive due to some existing sanitization. In this second example, again a query parameter is read from let path = require('path');
express().get('/data', (req, res) => {
let file = path.join(HOME_DIR, 'public', req.query.file);
res.sendFile(file);
}); If the query reported the call to Note that both examples are correctly handled by the standard taint tracking library and security queries. References
javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-078/CommandInjection.qhelpUncontrolled command line with additional heuristic sourcesCode that passes user input directly to RecommendationIf possible, use hard-coded string literals to specify the command to run or library to load. Instead of passing the user input directly to the process or library function, examine the user input and then choose among hard-coded string literals. If the applicable libraries or commands cannot be determined at compile time, then add code to verify that the user input string is safe before using it. ExampleThe following example shows code that takes a shell script that can be changed maliciously by a user, and passes it straight to var cp = require("child_process"),
http = require('http'),
url = require('url');
var server = http.createServer(function(req, res) {
let cmd = url.parse(req.url, true).query.path;
cp.exec(cmd); // BAD
}); References
javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-079/Xss.qhelpClient-side cross-site scripting with additional heuristic sourcesDirectly writing user input (for example, a URL query parameter) to a webpage without properly sanitizing the input first, allows for a cross-site scripting vulnerability. This kind of vulnerability is also called DOM-based cross-site scripting, to distinguish it from other types of cross-site scripting. RecommendationTo guard against cross-site scripting, consider using contextual output encoding/escaping before writing user input to the page, or one of the other solutions that are mentioned in the references. ExampleThe following example shows part of the page URL being written directly to the document, leaving the website vulnerable to cross-site scripting. function setLanguageOptions() {
var href = document.location.href,
deflt = href.substring(href.indexOf("default=")+8);
document.write("<OPTION value=1>"+deflt+"</OPTION>");
document.write("<OPTION value=2>English</OPTION>");
} References
javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-089/SqlInjection.qhelpDatabase query built from user-controlled sources with additional heuristic sourcesIf a database query (such as a SQL or NoSQL query) is built from user-provided data without sufficient sanitization, a malicious user may be able to run malicious database queries. RecommendationMost database connector libraries offer a way of safely embedding untrusted data into a query by means of query parameters or prepared statements. For NoSQL queries, make use of an operator like MongoDB's ExampleIn the following example, assume the function The handler constructs two copies of the same SQL query involving user input taken from the request object, once unsafely using string concatenation, and once safely using query parameters. In the first case, the query string In the second case, the parameter is embedded into the query string const app = require("express")(),
pg = require("pg"),
pool = new pg.Pool(config);
app.get("search", function handler(req, res) {
// BAD: the category might have SQL special characters in it
var query1 =
"SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" +
req.params.category +
"' ORDER BY PRICE";
pool.query(query1, [], function(err, results) {
// process results
});
// GOOD: use parameters
var query2 =
"SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY=$1" + " ORDER BY PRICE";
pool.query(query2, [req.params.category], function(err, results) {
// process results
});
}); References
javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-094/CodeInjection.qhelpCode injection with additional heuristic sourcesDirectly evaluating user input (for example, an HTTP request parameter) as code without properly sanitizing the input first allows an attacker arbitrary code execution. This can occur when user input is treated as JavaScript, or passed to a framework which interprets it as an expression to be evaluated. Examples include AngularJS expressions or JQuery selectors. RecommendationAvoid including user input in any expression which may be dynamically evaluated. If user input must be included, use context-specific escaping before including it. It is important that the correct escaping is used for the type of evaluation that will occur. ExampleThe following example shows part of the page URL being evaluated as JavaScript code. This allows an attacker to provide JavaScript within the URL. If an attacker can persuade a user to click on a link to such a URL, the attacker can evaluate arbitrary JavaScript in the browser of the user to, for example, steal cookies containing session information. eval(document.location.href.substring(document.location.href.indexOf("default=")+8)) The following example shows a Pug template being constructed from user input, allowing attackers to run arbitrary code via a payload such as const express = require('express')
var pug = require('pug');
const app = express()
app.post('/', (req, res) => {
var input = req.query.username;
var template = `
doctype
html
head
title= 'Hello world'
body
form(action='/' method='post')
input#name.form-control(type='text)
button.btn.btn-primary(type='submit') Submit
p Hello `+ input
var fn = pug.compile(template);
var html = fn();
res.send(html);
}) Below is an example of how to use a template engine without any risk of template injection. The user input is included via an interpolation expression const express = require('express')
var pug = require('pug');
const app = express()
app.post('/', (req, res) => {
var input = req.query.username;
var template = `
doctype
html
head
title= 'Hello world'
body
form(action='/' method='post')
input#name.form-control(type='text)
button.btn.btn-primary(type='submit') Submit
p Hello #{username}`
var fn = pug.compile(template);
var html = fn({username: input});
res.send(html);
}) References
javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-117/LogInjection.qhelpLog injection with additional heuristic sourcesIf unsanitized user input is written to a log entry, a malicious user may be able to forge new log entries. Forgery can occur if a user provides some input with characters that are interpreted when the log output is displayed. If the log is displayed as a plain text file, then new line characters can be used by a malicious user. If the log is displayed as HTML, then arbitrary HTML may be included to spoof log entries. RecommendationUser input should be suitably sanitized before it is logged. If the log entries are in plain text then line breaks should be removed from user input, using For log entries that will be displayed in HTML, user input should be HTML-encoded before being logged, to prevent forgery and other forms of HTML injection. ExampleIn the first example, a username, provided by the user, is logged using `console.info`. In the first case, it is logged without any sanitization. In the second case, the username is used to build an error that is logged using `console.error`. If a malicious user provides `username=Guest%0a[INFO]+User:+Admin%0a` as a username parameter, the log entry will be splitted in two different lines, where the second line will be `[INFO]+User:+Admin`. const http = require('http');
const url = require('url');
const server = http.createServer((req, res) => {
let q = url.parse(req.url, true);
console.info(`[INFO] User: ${q.query.username}`); // BAD: User input logged as-is
})
server.listen(3000, '127.0.0.1', () => {}); In the second example, const http = require('http');
const url = require('url');
const server = http.createServer((req, res) => {
let q = url.parse(req.url, true);
// GOOD: remove newlines from user controlled input before logging
let username = q.query.username.replace(/\n|\r/g, "");
console.info(`[INFO] User: ${username}`);
});
server.listen(3000, '127.0.0.1', () => {}); References
javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-134/TaintedFormatString.qhelpUse of externally-controlled format string with additional heuristic sourcesFunctions like the Node.js standard library function RecommendationEither sanitize the input before including it in the format string, or use a ExampleThe following program snippet logs information about an unauthorized access attempt. The log message includes the user name, and the user's IP address is passed as an additional argument to const app = require("express")();
app.get("unauthorized", function handler(req, res) {
let user = req.query.user;
let ip = req.connection.remoteAddress;
console.log("Unauthorized access attempt by " + user, ip);
}); However, if a malicious user provides Instead, the user name should be included using the const app = require("express")();
app.get("unauthorized", function handler(req, res) {
let user = req.query.user;
let ip = req.connection.remoteAddress;
console.log("Unauthorized access attempt by %s", user, ip);
}); References
javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-346/CorsMisconfigurationForCredentials.qhelpCORS misconfiguration for credentials transfer with additional heuristic sourcesA server can send the When the RecommendationWhen the Since the ExampleIn the example below, the server allows the browser to send user credentials in a Cross-Origin request. The request header var https = require('https'),
url = require('url');
var server = https.createServer(function(){});
server.on('request', function(req, res) {
let origin = url.parse(req.url, true).query.origin;
// BAD: attacker can choose the value of origin
res.setHeader("Access-Control-Allow-Origin", origin);
res.setHeader("Access-Control-Allow-Credentials", true);
// ...
}); This is not secure, since an attacker can choose the value of the var https = require('https'),
url = require('url');
var server = https.createServer(function(){});
server.on('request', function(req, res) {
let origin = url.parse(req.url, true).query.origin,
whitelist = {
"https://example.com": true,
"https://subdomain.example.com": true,
"https://example.com:1337": true
};
if (origin in whitelist) {
// GOOD: the origin is in the whitelist
res.setHeader("Access-Control-Allow-Origin", origin);
res.setHeader("Access-Control-Allow-Credentials", true);
}
// ...
}); References
javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-400/RemotePropertyInjection.qhelpRemote property injection with additional heuristic sourcesDynamically computing object property names from untrusted input may have multiple undesired consequences. For example, if the property access is used as part of a write, an attacker may overwrite vital properties of objects, such as Moreover, if the name of an HTTP header is user-controlled, an attacker may exploit this to overwrite security-critical headers such as RecommendationThe most common case in which prototype pollution vulnerabilities arise is when JavaScript objects are used for implementing map data structures. This case should be avoided whenever possible by using the ECMAScript 2015 When using user input as part of a header name, a sanitization step should be performed on the input to ensure that the name does not clash with existing header names such as ExampleIn the example below, the dynamically computed property var express = require('express');
var app = express();
var myObj = {}
app.get('/user/:id', function(req, res) {
var prop = req.query.userControlled; // BAD
myObj[prop] = function() {};
console.log("Request object " + myObj);
}); This is not secure since an attacker may exploit this code to overwrite the property var express = require('express');
var app = express();
var myObj = {}
app.get('/user/:id', function(req, res) {
var prop = "$" + req.query.userControlled; // GOOD
myObj[prop] = function() {};
console.log("Request object " + myObj);
}); References
javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-502/UnsafeDeserialization.qhelpDeserialization of user-controlled data with additional heuristic sourcesDeserializing untrusted data using any deserialization framework that allows the construction of arbitrary functions is easily exploitable and, in many cases, allows an attacker to execute arbitrary code. RecommendationAvoid deserialization of untrusted data if at all possible. If the architecture permits it, then use formats like JSON or XML that cannot represent functions. When using YAML or other formats that support the serialization and deserialization of functions, ensure that the parser is configured to disable deserialization of arbitrary functions. ExampleThe following example calls the const app = require("express")(),
jsyaml = require("js-yaml");
app.get("load", function(req, res) {
let data = jsyaml.load(req.params.data);
// ...
}); Using the const app = require("express")(),
jsyaml = require("js-yaml");
app.get("load", function(req, res) {
let data = jsyaml.safeLoad(req.params.data);
// ...
}); References
javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-611/Xxe.qhelpXML external entity expansion with additional heuristic sourcesParsing untrusted XML files with a weakly configured XML parser may lead to an XML External Entity (XXE) attack. This type of attack uses external entity references to access arbitrary files on a system, carry out denial-of-service (DoS) attacks, or server-side request forgery. Even when the result of parsing is not returned to the user, DoS attacks are still possible and out-of-band data retrieval techniques may allow attackers to steal sensitive data. RecommendationThe easiest way to prevent XXE attacks is to disable external entity handling when parsing untrusted data. How this is done depends on the library being used. Note that some libraries, such as recent versions of ExampleThe following example uses the const app = require("express")(),
libxml = require("libxmljs");
app.post("upload", (req, res) => {
let xmlSrc = req.body,
doc = libxml.parseXml(xmlSrc, { noent: true });
}); To guard against XXE attacks, the const app = require("express")(),
libxml = require("libxmljs");
app.post("upload", (req, res) => {
let xmlSrc = req.body,
doc = libxml.parseXml(xmlSrc);
}); References
javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-643/XpathInjection.qhelpXPath injection with additional heuristic sourcesIf an XPath expression is built using string concatenation, and the components of the concatenation include user input, it makes it very easy for a user to create a malicious XPath expression. RecommendationIf user input must be included in an XPath expression, either sanitize the data or use variable references to safely embed it without altering the structure of the expression. ExampleIn this example, the code accepts a user name specified by the user, and uses this unvalidated and unsanitized value in an XPath expression constructed using the const express = require('express');
const xpath = require('xpath');
const app = express();
app.get('/some/route', function(req, res) {
let userName = req.param("userName");
// BAD: Use user-provided data directly in an XPath expression
let badXPathExpr = xpath.parse("//users/user[login/text()='" + userName + "']/home_dir/text()");
badXPathExpr.select({
node: root
});
}); Instead, embed the user input using the variable replacement mechanism offered by const express = require('express');
const xpath = require('xpath');
const app = express();
app.get('/some/route', function(req, res) {
let userName = req.param("userName");
// GOOD: Embed user-provided data using variables
let goodXPathExpr = xpath.parse("//users/user[login/text()=$userName]/home_dir/text()");
goodXPathExpr.select({
node: root,
variables: { userName: userName }
});
}); References
javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-730/RegExpInjection.qhelpRegular expression injection with additional heuristic sourcesConstructing a regular expression with unsanitized user input is dangerous as a malicious user may be able to modify the meaning of the expression. In particular, such a user may be able to provide a regular expression fragment that takes exponential time in the worst case, and use that to perform a Denial of Service attack. RecommendationBefore embedding user input into a regular expression, use a sanitization function such as lodash's ExampleThe following example shows a HTTP request parameter that is used to construct a regular expression without sanitizing it first: var express = require('express');
var app = express();
app.get('/findKey', function(req, res) {
var key = req.param("key"), input = req.param("input");
// BAD: Unsanitized user input is used to construct a regular expression
var re = new RegExp("\\b" + key + "=(.*)\n");
}); Instead, the request parameter should be sanitized first, for example using the function var express = require('express');
var _ = require('lodash');
var app = express();
app.get('/findKey', function(req, res) {
var key = req.param("key"), input = req.param("input");
// GOOD: User input is sanitized before constructing the regex
var safeKey = _.escapeRegExp(key);
var re = new RegExp("\\b" + safeKey + "=(.*)\n");
}); References
javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-770/ResourceExhaustion.qhelpResource exhaustion with additional heuristic sourcesApplications are constrained by how many resources they can make use of. Failing to respect these constraints may cause the application to be unresponsive or crash. It is therefore problematic if attackers can control the sizes or lifetimes of allocated objects. RecommendationEnsure that attackers can not control object sizes and their lifetimes. If object sizes and lifetimes must be controlled by external parties, ensure you restrict the object sizes and lifetimes so that they are within acceptable ranges. ExampleThe following example allocates a buffer with a user-controlled size. var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
var size = parseInt(url.parse(req.url, true).query.size);
let buffer = Buffer.alloc(size); // BAD
// ... use the buffer
}); This is problematic since an attacker can choose a size that makes the application run out of memory. Even worse, in older versions of Node.js, this could leak confidential memory. To prevent such attacks, limit the buffer size: var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
var size = parseInt(url.parse(req.url, true).query.size);
if (size > 1024) {
res.statusCode = 400;
res.end("Bad request.");
return;
}
let buffer = Buffer.alloc(size); // GOOD
// ... use the buffer
}); ExampleAs another example, consider an application that allocates an array with a user-controlled size, and then fills it with values: var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
var size = parseInt(url.parse(req.url, true).query.size);
let dogs = new Array(size).fill("dog"); // BAD
// ... use the dog
}); The allocation of the array itself is not problematic since arrays are allocated sparsely, but the subsequent filling of the array will take a long time, causing the application to be unresponsive, or even run out of memory. Again, a limit on the size will prevent the attack: var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
var size = parseInt(url.parse(req.url, true).query.size);
if (size > 1024) {
res.statusCode = 400;
res.end("Bad request.");
return;
}
let dogs = new Array(size).fill("dog"); // GOOD
// ... use the dogs
}); ExampleFinally, the following example lets a user choose a delay after which a function is executed: var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
var delay = parseInt(url.parse(req.url, true).query.delay);
setTimeout(f, delay); // BAD
}); This is problematic because a large delay essentially makes the application wait indefinitely before executing the function. Repeated registrations of such delays will therefore use up all of the memory in the application. A limit on the delay will prevent the attack: var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
var delay = parseInt(url.parse(req.url, true).query.delay);
if (delay > 1000) {
res.statusCode = 400;
res.end("Bad request.");
return;
}
setTimeout(f, delay); // GOOD
}); References
javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-776/XmlBomb.qhelpXML internal entity expansion with additional heuristic sourcesParsing untrusted XML files with a weakly configured XML parser may be vulnerable to denial-of-service (DoS) attacks exploiting uncontrolled internal entity expansion. In XML, so-called internal entities are a mechanism for introducing an abbreviation for a piece of text or part of a document. When a parser that has been configured to expand entities encounters a reference to an internal entity, it replaces the entity by the data it represents. The replacement text may itself contain other entity references, which are expanded recursively. This means that entity expansion can increase document size dramatically. If untrusted XML is parsed with entity expansion enabled, a malicious attacker could submit a document that contains very deeply nested entity definitions, causing the parser to take a very long time or use large amounts of memory. This is sometimes called an XML bomb attack. RecommendationThe safest way to prevent XML bomb attacks is to disable entity expansion when parsing untrusted data. How this is done depends on the library being used. Note that some libraries, such as recent versions of ExampleThe following example uses the XML parser provided by the const app = require("express")(),
expat = require("node-expat");
app.post("upload", (req, res) => {
let xmlSrc = req.body,
parser = new expat.Parser();
parser.on("startElement", handleStart);
parser.on("text", handleText);
parser.write(xmlSrc);
}); At the time of writing, const app = require("express")(),
sax = require("sax");
app.post("upload", (req, res) => {
let xmlSrc = req.body,
parser = sax.parser(true);
parser.onopentag = handleStart;
parser.ontext = handleText;
parser.write(xmlSrc);
}); References
javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-807/ConditionalBypass.qhelpUser-controlled bypass of security check with additional heuristic sourcesUsing user-controlled data in a permissions check may allow a user to gain unauthorized access to protected functionality or data. RecommendationWhen checking whether a user is authorized for a particular activity, do not use data that is entirely controlled by that user in the permissions check. If necessary, always validate the input, ideally against a fixed list of expected values. Similarly, do not decide which permission to check for, based on user data. In particular, avoid using computation to decide which permissions to check for. Use fixed permissions for particular actions, rather than generating the permission to check for. ExampleIn this example, we have a server that shows private information for a user, based on the request parameter var express = require('express');
var app = express();
// ...
app.get('/full-profile/:userId', function(req, res) {
if (req.cookies.loggedInUserId !== req.params.userId) {
// BAD: login decision made based on user controlled data
requireLogin();
} else {
// ... show private information
}
}); This security check is, however, insufficient since an attacker can craft his cookie values to match those of any user. To prevent this, the server can cryptographically sign the security critical cookie values: var express = require('express');
var app = express();
// ...
app.get('/full-profile/:userId', function(req, res) {
if (req.signedCookies.loggedInUserId !== req.params.userId) {
// GOOD: login decision made based on server controlled data
requireLogin();
} else {
// ... show private information
}
}); Referencesjavascript/ql/src/experimental/heuristics/ql/src/Security/CWE-915/PrototypePollutingAssignment.qhelpPrototype-polluting assignment with additional heuristic sourcesMost JavaScript objects inherit the properties of the built-in One way to cause prototype pollution is by modifying an object obtained via a user-controlled property name. Most objects have a special RecommendationUse an associative data structure that is resilient to untrusted key values, such as a Map. In some cases, a prototype-less object created with Object.create(null) may be preferable. Alternatively, restrict the computed property name so it can't clash with a built-in property, either by prefixing it with a constant string, or by rejecting inputs that don't conform to the expected format. ExampleIn the example below, the untrusted value let express = require('express');
let app = express()
app.put('/todos/:id', (req, res) => {
let id = req.params.id;
let items = req.session.todos[id];
if (!items) {
items = req.session.todos[id] = {};
}
items[req.query.name] = req.query.text;
res.end(200);
}); One way to fix this is to use Map objects to associate key/value pairs instead of regular objects, as shown below: let express = require('express');
let app = express()
app.put('/todos/:id', (req, res) => {
let id = req.params.id;
let items = req.session.todos.get(id);
if (!items) {
items = new Map();
req.sessions.todos.set(id, items);
}
items.set(req.query.name, req.query.text);
res.end(200);
}); Referencespython/ql/src/Imports/SyntaxError.qhelpSyntax errorSyntax errors prevent a module being evaluated and thus imported. An attempt to import a module with invalid syntax will fail; a A common cause of syntax errors is the difference in syntax between Python 2 and Python 3. In particular, a syntax error may be alerted if a Python 3 file is assumed to be compatible with Python 2 (or vice versa). Explicitly specifying the expected Python version can help prevent this. The existence of a syntax error in a module may suggest other problems as well. Either the module is never imported in practice and could be deleted or a RecommendationFixing the syntax error is the obvious fix. However, it is worth investigating why a module containing a syntax error was able to persist and address that problem as well. If you suspect that the syntax error is caused by the analysis using the wrong version of Python, consider specifying the version explicitly. When you run code scanning using the CodeQL action, you can configure the Python version to use. For more information, see Analyzing Python dependencies. References
python/ql/src/Security/CWE-326/WeakCryptoKey.qhelpUse of weak cryptographic keyModern encryption relies on it being computationally infeasible to break the cipher and decode a message without the key. As computational power increases, the ability to break ciphers grows and keys need to become larger. The three main asymmetric key algorithms currently in use are Rivest–Shamir–Adleman (RSA) cryptography, Digital Signature Algorithm (DSA), and Elliptic-curve cryptography (ECC). With current technology, key sizes of 2048 bits for RSA and DSA, or 256 bits for ECC, are regarded as unbreakable. RecommendationIncrease the key size to the recommended amount or larger. For RSA or DSA this is at least 2048 bits, for ECC this is at least 256 bits. References
python/ql/src/Security/CWE-730/PolynomialReDoS.qhelpPolynomial regular expression used on uncontrolled dataSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length n is proportional to nk or even 2n. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service ("DoS") attack by crafting an expensive input string for the regular expression to match. The regular expression engine provided by Python uses a backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower. Typically, a regular expression is affected by this problem if it contains a repetition of the form RecommendationModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter. ExampleConsider this use of a regular expression, which removes all leading and trailing whitespace in a string: re.sub(r"^\s+|\s+$", "", text) # BAD
The sub-expression This ultimately means that the time cost of trimming a string is quadratic in the length of the string. So a string like Avoid this problem by rewriting the regular expression to not contain the ambiguity about when to start matching whitespace sequences. For instance, by using a negative look-behind ( Note that the sub-expression ExampleAs a similar, but slightly subtler problem, consider the regular expression that matches lines with numbers, possibly written using scientific notation: ^0\.\d+E?\d+$ # BAD
The problem with this regular expression is in the sub-expression This is problematic for strings that do not end with a digit. Such a string will force the regular expression engine to process each digit sequence once per digit in the sequence, again leading to a quadratic time complexity. To make the processing faster, the regular expression should be rewritten such that the two References
python/ql/src/Security/CWE-730/ReDoS.qhelpInefficient regular expressionSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length n is proportional to nk or even 2n. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service ("DoS") attack by crafting an expensive input string for the regular expression to match. The regular expression engine provided by Python uses a backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower. Typically, a regular expression is affected by this problem if it contains a repetition of the form RecommendationModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter. ExampleConsider this regular expression: ^_(__|.)+_$
Its sub-expression This problem can be avoided by rewriting the regular expression to remove the ambiguity between the two branches of the alternative inside the repetition: ^_(__|[^_])+_$
References
python/ql/src/experimental/Security/CWE-022bis/UnsafeUnpack.qhelpArbitrary file write during a tarball extraction from a user controlled sourceExtracting files from a malicious tarball without validating that the destination file path is within the destination directory using Tarball contain archive entries representing each file in the archive. These entries include a file path for the entry, but these file paths are not restricted and may contain unexpected special elements such as the directory traversal element ( For example, if a tarball contains a file entry RecommendationEnsure that output paths constructed from tarball entries are validated to prevent writing files to unexpected locations. Consider using a safer module, such as: ExampleIn this example an archive is extracted without validating file paths. import requests
import shutil
url = "https://www.someremote.location/tarball.tar.gz"
response = requests.get(url, stream=True)
tarpath = "/tmp/tmp456/tarball.tar.gz"
with open(tarpath, "wb") as f:
f.write(response.raw.read())
untarredpath = "/tmp/tmp123"
shutil.unpack_archive(tarpath, untarredpath) To fix this vulnerability, we need to call the function import requests
import tarfile
url = "https://www.someremote.location/tarball.tar.gz"
response = requests.get(url, stream=True)
tarpath = "/tmp/tmp456/tarball.tar.gz"
with open(tarpath, "wb") as f:
f.write(response.raw.read())
untarredpath = "/tmp/tmp123"
with tarfile.open(tarpath) as tar:
for member in tar.getmembers():
if member.name.startswith("/") or ".." in member.name:
raise Exception("Path traversal identified in tarball")
tar.extract(untarredpath, member) References
ruby/ql/src/queries/security/cwe-020/MissingFullAnchor.qhelpBadly anchored regular expressionRegular expressions in Ruby can use anchors to match the beginning and end of a string. However, if the RecommendationUse the ExampleThe following (bad) example code uses a regular expression to check that a string contains only digits. def bad(input)
raise "Bad input" unless input =~ /^[0-9]+$/
# ....
end The regular expression def good(input)
raise "Bad input" unless input =~ /\A[0-9]+\z/
# ....
end Referencesruby/ql/src/queries/security/cwe-078/KernelOpen.qhelpUse of
|
|
Java: Add XSS Sanitizer for `HttpServletResponse.setContentType` with safe values
This is a result of the commit "SSA: Fix bug in guards for ssa input nodes."
C#: Proper handling of value tuples in `cs/call-to-object-tostring`.
Rust: Test and model some string and iterator methods
Reported here: #17743 Without this change on the query provided by the user: ``` [2025-02-25 12:42:01] Evaluated non-recursive predicate quickquery::UnrealFunctionAnnotation.annotates/1#dispred#9cd6c269@c668c8tv in 23846ms (size: 20381473). Evaluated relational algebra for predicate quickquery::UnrealFunctionAnnotation.annotates/1#dispred#9cd6c269@c668c8tv with tuple counts: 1 ~0% {0} r1 = CONSTANT()[] 27323 ~0% {2} | JOIN WITH `Location::Location.getEndLine/0#dispred#83af84ae#bf` CARTESIAN PRODUCT OUTPUT Rhs.0, Rhs.1 6162566035 ~0% {4} | JOIN WITH `Location::Location.getStartLine/0#d54f9e6c` CARTESIAN PRODUCT OUTPUT Lhs.0, Lhs.1, Rhs.0, Rhs.1 {4} | REWRITE WITH TEST InOut.1 < InOut.3 3894825644 ~5% {2} | SCAN OUTPUT In.2, In.0 73148692 ~0% {3} | JOIN WITH fun_decls_40#join_rhs ON FIRST 1 OUTPUT Lhs.1, Lhs.0, Rhs.1 73148692 ~0% {4} | JOIN WITH `Location::Location.getFile/0#dispred#d1f8b5d1` ON FIRST 1 OUTPUT Lhs.1, Rhs.1, Lhs.0, Lhs.2 864579 ~0% {2} | JOIN WITH `Location::Location.getFile/0#dispred#d1f8b5d1` ON FIRST 2 OUTPUT Lhs.2, Lhs.3 13010742 ~1% {2} | JOIN WITH macroinvocations_20#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.1 20653781 ~0% {3} | JOIN WITH `Macro::MacroAccess.getOutermostMacroAccess/0#d58b05db_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, _, Lhs.1 20653781 ~4% {3} | REWRITE WITH Out.1 := 1 20381473 ~8% {2} | JOIN WITH macroinvocations_03#join_rhs ON FIRST 2 OUTPUT Lhs.0, Lhs.2 return r1 ``` With this change: ``` [2025-02-25 12:43:10] Evaluated non-recursive predicate quickquery::UnrealFunctionAnnotation.annotates/1#dispred#9cd6c269@11bf8956 in 928ms (size: 20381473). Evaluated relational algebra for predicate quickquery::UnrealFunctionAnnotation.annotates/1#dispred#9cd6c269@11bf8956 with tuple counts: 6873 ~3% {2} r1 = SCAN fun_decls OUTPUT In.4, In.0 6857 ~0% {3} | JOIN WITH `Location::Location.getStartLine/0#d54f9e6c` ON FIRST 1 OUTPUT Lhs.0, Lhs.1, Rhs.1 6857 ~2% {3} | JOIN WITH `Location::Location.getFile/0#dispred#d1f8b5d1` ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.2 6193961 ~0% {3} | JOIN WITH `Location::Location.getFile/0#dispred#d1f8b5d1_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.2 27389714 ~1% {4} | JOIN WITH macroinvocations_20#join_rhs ON FIRST 1 OUTPUT Lhs.0, Lhs.1, Lhs.2, Rhs.1 27389714 ~1% {4} | JOIN WITH locations_default ON FIRST 1 OUTPUT Lhs.1, Lhs.2, Lhs.3, Rhs.4 {4} | REWRITE WITH TEST InOut.3 < InOut.1 13010742 ~1% {2} | SCAN OUTPUT In.2, In.0 20653781 ~0% {3} | JOIN WITH `Macro::MacroAccess.getOutermostMacroAccess/0#d58b05db_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, _, Lhs.1 20653781 ~4% {3} | REWRITE WITH Out.1 := 1 20381473 ~8% {2} | JOIN WITH macroinvocations_03#join_rhs ON FIRST 2 OUTPUT Lhs.0, Lhs.2 return r1 ```
…cess Java: StaticInitializationVector with postprocess
Co-authored-by: Asgerf <[email protected]>
Java: Switch BaseSSA to use shared SSA lib.
Ssa: Refactor the data flow integration module
Co-authored-by: Asgerf <[email protected]>
C++: Fix join-order problem with `isBefore`
Rust: Update some inline expectation comments
JS: Support 'response' threat model and @tanstack/react-query
Add queries to C# CCR suite
Test: Add support for RelatedLocation tag and use in a JS query
See Commits and Changes for more details.
Created by
pull[bot]
Can you help keep this open source service alive? 💖 Please sponsor : )