diff --git a/internal/services/engines/dart/rules.go b/internal/services/engines/dart/rules.go index 9831e7e53..cb63c921e 100644 --- a/internal/services/engines/dart/rules.go +++ b/internal/services/engines/dart/rules.go @@ -160,9 +160,9 @@ func NewNoLogSensitive() text.TextRule { }, Type: text.Regular, Expressions: []*regexp.Regexp{ - regexp.MustCompile(`print\(.*\$`), - regexp.MustCompile(`window\.console.*\(`), - regexp.MustCompile(`log.*\.(finest|finer|fine|config|info|warning|severe|shout|erro).*\(`), + regexp.MustCompile(`print\(.*(\$|%|('|")\s*\+)`), + regexp.MustCompile(`window\.console.*\(.*(\$|%|('|")\s*\+)`), + regexp.MustCompile(`log.*\.(finest|finer|fine|config|info|warning|severe|shout|erro).*\(.*(\$|%|('|")\s*\+)`), }, } } @@ -278,10 +278,10 @@ func NewNoUseCipherMode() text.TextRule { }, Type: text.Regular, Expressions: []*regexp.Regexp{ - regexp.MustCompile(`(?i)AesMode\.ECB`), - regexp.MustCompile(`(?i)AesMode\.OFB`), - regexp.MustCompile(`(?i)AesMode\.CTS`), - regexp.MustCompile(`(?i)AesMode\.CFB`), + regexp.MustCompile(`(?i)(AesMode\.ECB)`), + regexp.MustCompile(`(?i)(AesMode\.OFB)`), + regexp.MustCompile(`(?i)(AesMode\.CTS)`), + regexp.MustCompile(`(?i)(AesMode\.CFB)`), }, } } diff --git a/internal/services/engines/dart/rules_test.go b/internal/services/engines/dart/rules_test.go index 1f72f1047..5695e27af 100644 --- a/internal/services/engines/dart/rules_test.go +++ b/internal/services/engines/dart/rules_test.go @@ -107,6 +107,167 @@ func TestRulesVulnerableCode(t *testing.T) { }, }, }, + { + Name: "HS-DART-7", + Rule: NewXSSAttack(), + Src: SampleVulnerableXSSAttack, + Findings: []engine.Finding{ + { + CodeSample: "var element = new Element.html(sprintf(\"
%s
\", [content]));", + SourceLocation: engine.Location{ + Line: 8, + Column: 19, + }, + }, + }, + }, + { + Name: "HS-DART-8", + Rule: NewNoLogSensitive(), + Src: SampleVulnerableNoLogSensitive, + Findings: []engine.Finding{ + { + CodeSample: "print(sprintf(\"User identity is: %s\", [identity]));", + SourceLocation: engine.Location{ + Line: 9, + Column: 1, + }, + }, + { + CodeSample: "_logger.info(sprintf(\"User identity is: %s\", [identity]));", + SourceLocation: engine.Location{ + Line: 11, + Column: 2, + }, + }, + }, + }, + { + Name: "HS-DART-9", + Rule: NewWeakHashingFunctionMd5OrSha1(), + Src: SampleVulnerableWeakHashingFunctionMd5OrSha1, + Findings: []engine.Finding{ + { + CodeSample: "var digest = md5.convert(content);", + SourceLocation: engine.Location{ + Line: 11, + Column: 15, + }, + }, + }, + }, + { + Name: "HS-DART-10", + Rule: NewNoUseSelfSignedCertificate(), + Src: SampleVulnerableNoUseSelfSignedCertificate, + Findings: []engine.Finding{ + { + CodeSample: "context.setTrustedCertificates(\"client.cer\");", + SourceLocation: engine.Location{ + Line: 4, + Column: 8, + }, + }, + }, + }, + { + Name: "HS-DART-11", + Rule: NewNoUseBiometricsTypeAndroid(), + Src: SampleVulnerableNoUseBiometricsTypeAndroid, + Findings: []engine.Finding{ + { + CodeSample: "authenticated = await auth.authenticateWithBiometrics(", + SourceLocation: engine.Location{ + Line: 4, + Column: 29, + }, + }, + }, + }, + { + Name: "HS-DART-12", + Rule: NewNoListClipboardChanges(), + Src: SampleVulnerableNoListClipboardChanges, + Findings: []engine.Finding{ + { + CodeSample: "Map result = await SystemChannels.platform.invokeMethod('Clipboard.getData');", + SourceLocation: engine.Location{ + Line: 4, + Column: 75, + }, + }, + }, + }, + { + Name: "HS-DART-13", + Rule: NewSQLInjection(), + Src: SampleVulnerableSQLInjection, + Findings: []engine.Finding{ + { + CodeSample: "List list = await database.rawQuery(\"SELECT * FROM Users WHERE username = '\" + username + \"';\");", + SourceLocation: engine.Location{ + Line: 10, + Column: 34, + }, + }, + }, + }, + { + Name: "HS-DART-14", + Rule: NewNoUseNSTemporaryDirectory(), + Src: SampleVulnerableNoUseNSTemporaryDirectory, + Findings: []engine.Finding{ + { + CodeSample: "let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true);", + SourceLocation: engine.Location{ + Line: 3, + Column: 49, + }, + }, + }, + }, + { + Name: "HS-DART-15", + Rule: NewNoUseCipherMode(), + Src: SampleVulnerableNoUseCipherMode, + Findings: []engine.Finding{ + { + CodeSample: "final encrypter = Encrypter(AES(key, mode: AESMode.cts));", + SourceLocation: engine.Location{ + Line: 3, + Column: 43, + }, + }, + }, + }, + { + Name: "HS-DART-16", + Rule: NewCorsAllowOriginWildCard(), + Src: SampleVulnerableCorsAllowOriginWildCard, + Findings: []engine.Finding{ + { + CodeSample: `request.response.headers.add("Access-Control-Allow-Origin", "*");`, + SourceLocation: engine.Location{ + Line: 9, + Column: 32, + }, + }, + }, + }, + { + Name: "HS-DART-17", + Rule: NewUsingShellInterpreterWhenExecutingOSCommand(), + Src: SampleVulnerableUsingShellInterpreterWhenExecutingOSCommand, + Findings: []engine.Finding{ + { + CodeSample: "var result = await Process.run(\"netcfg\", UserParams);", + SourceLocation: engine.Location{ + Line: 4, + Column: 20, + }, + }, + }, + }, } testutil.TestVulnerableCode(t, testcases) @@ -144,6 +305,61 @@ func TestRulesSafeCode(t *testing.T) { Rule: NewSendSMS(), Src: "", }, + { + Name: "HS-DART-7", + Rule: NewXSSAttack(), + Src: SampleSafeXSSAttack, + }, + { + Name: "HS-DART-8", + Rule: NewNoLogSensitive(), + Src: SampleSafeNoLogSensitive, + }, + { + Name: "HS-DART-9", + Rule: NewWeakHashingFunctionMd5OrSha1(), + Src: SampleSafeWeakHashingFunctionMd5OrSha1, + }, + { + Name: "HS-DART-10", + Rule: NewNoUseSelfSignedCertificate(), + Src: SampleSafeNoUseSelfSignedCertificate, + }, + { + Name: "HS-DART-11", + Rule: NewNoUseBiometricsTypeAndroid(), + Src: SampleSafeNoUseBiometricsTypeAndroid, + }, + { + Name: "HS-DART-12", + Rule: NewNoListClipboardChanges(), + Src: SampleSafeNoListClipboardChanges, + }, + { + Name: "HS-DART-13", + Rule: NewSQLInjection(), + Src: SampleSafeSQLInjection, + }, + { + Name: "HS-DART-14", + Rule: NewNoUseNSTemporaryDirectory(), + Src: SampleSafeNoUseNSTemporaryDirectory, + }, + { + Name: "HS-DART-15", + Rule: NewNoUseCipherMode(), + Src: SampleSafeNoUseCipherMode, + }, + { + Name: "HS-DART-16", + Rule: NewCorsAllowOriginWildCard(), + Src: SampleSafeCorsAllowOriginWildCard, + }, + { + Name: "HS-DART-17", + Rule: NewUsingShellInterpreterWhenExecutingOSCommand(), + Src: SampleSafeUsingShellInterpreterWhenExecutingOSCommand, + }, } testutil.TestSafeCode(t, testcases) diff --git a/internal/services/engines/dart/sample_test.go b/internal/services/engines/dart/sample_test.go index 172ca3442..d314e8fa5 100644 --- a/internal/services/engines/dart/sample_test.go +++ b/internal/services/engines/dart/sample_test.go @@ -91,6 +91,135 @@ static Future SentToApi( // Possible vulnerable code: HTTP without SSL is not secure. return _HttpServer.bindSecure('http://my-api.com.br', port, context, backlog, v6Only, requestClientCertificate, shared); } +` + SampleVulnerableXSSAttack = ` +import 'package:sprintf/sprintf.dart'; +import 'dart:html'; +... + +void RenderHTML(String content) { + // Possible vulnerable code: In your html you can receive variable and sent to html render in this case occurs XSS attack + var element = new Element.html(sprintf("
%s
", [content])); + document.body.append(element); +} +` + SampleVulnerableNoLogSensitive = ` +import 'package:sprintf/sprintf.dart'; +import 'package:logging/logging.dart'; +... +final _logger = Logger('YourClassName'); + +void ShowUserSensitiveInformation(String identity) { + // Possible vulnerable code: Logging Sensitive information is not good implementation + print(sprintf("User identity is: %s", [identity])); + // or Possible vulnerable code: Logging Sensitive information is not good implementation + _logger.info(sprintf("User identity is: %s", [identity])); + sentToAPIUserIdentity(identity); +} +` + SampleVulnerableWeakHashingFunctionMd5OrSha1 = ` +import 'dart:convert'; +import 'package:convert/convert.dart'; +import 'package:crypto/crypto.dart' as crypto; + +///Generate MD5 hash +generateMd5(String data) { + var content = new Utf8Encoder().convert(data); + var md5 = crypto.md5; + // Possible vulnerable code: This code is bad because this type cryptography is easy of to be broken. + var digest = md5.convert(content); + return hex.encode(digest.bytes); +} +` + SampleVulnerableNoUseSelfSignedCertificate = ` +final SecurityContext context = SecurityContext(withTrustedRoots: false); +// Possible vulnerable code: This code is bad because if you can exposed for MITM attacks +context.setTrustedCertificates("client.cer"); +Socket socket = await Socket.connect(serverIp, port); +socket = await SecureSocket.secure(socket, host: "server" + , context: context, onBadCertificate: (cert) => true); +` + SampleVulnerableNoUseBiometricsTypeAndroid = ` +try { +// Possible vulnerable code: This code is bad because your authentication can be passed easy form when exists only 1 method to authenticate + authenticated = await auth.authenticateWithBiometrics( + localizedReason: 'Touch your finger on the sensor to login', + useErrorDialogs: true, + stickyAuth: false + ); +} catch (e) { + print("error using biometric auth: $e"); +} +` + SampleVulnerableNoListClipboardChanges = ` +_getFromClipboard() async { + // Possible vulnerable code: Is not good idea read content from clipboard. + Map result = await SystemChannels.platform.invokeMethod('Clipboard.getData'); + if (result != null) { + return result['text'].toString(); + } + return ''; +} + +void sendToAPIToKeepChangesInDatabase() { + try { + String changesFromClipboard = await _getFromClipboard() + if (changesFromClipboard != "" { + // Here occurs SQL Injection, XSS attack, and others many forms to users attack your base of data when you safe content without treatment. + SaveChangesFromClipboardOnDatabasePost(changesFromClipboard) + } + } on HttpException { + ... + } +} +` + SampleVulnerableSQLInjection = ` +Database database = await openDatabase(path, version: 1, + onCreate: (Database db, int version) async { + await db.execute('CREATE TABLE Users (id INTEGER PRIMARY KEY, username TEXT, password TEXT);'); +}); + +getCheckIfUserExists(String username) { + try { + // Possible vulnerable code: User can be pass malicious code and delete all data from your database by example. + List list = await database.rawQuery("SELECT * FROM Users WHERE username = '" + username + "';"); + ... + } on Exception { + ... + } +} +` + SampleVulnerableNoUseNSTemporaryDirectory = ` +// Possible vulnerable code: If You get NSTemporaryDirectory you can get anywhere content from this directory +let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true); +` + SampleVulnerableNoUseCipherMode = ` +// Possible vulnerable code: This code is bad because this type cryptography is easy of to be broken. +final encrypter = Encrypter(AES(key, mode: AESMode.cts)); +` + SampleVulnerableCorsAllowOriginWildCard = ` +HttpServer.bind('127.0.0.1', 8080).then((server){ + server.listen((HttpRequest request){ + request.uri.queryParameters.forEach((param,val){ + print(param + '-' + val); + }); + + // Possible vulnerable code: When you allow any origin you can exposed to multiple attacks in your application + request.response.headers.add("Access-Control-Allow-Origin", "*"); + request.response.headers.add("Access-Control-Allow-Methods", "POST,GET,DELETE,PUT,OPTIONS"); + + request.response.statusCode = HttpStatus.OK; + request.response.write("Success!"); + request.response.close(); + }); +}); +` + SampleVulnerableUsingShellInterpreterWhenExecutingOSCommand = ` +getIPFromLoggedUser (List UserParams) async { + // Possible vulnerable code: User can be inject malicious code and run others commands after this command + var result = await Process.run("netcfg", UserParams); + return result.stdout +} ` ) @@ -142,5 +271,121 @@ static Future SentToApi( bool requestClientCertificate = false, bool shared = false} ) => _HttpServer.bindSecure('https://my-api.com.br', port, context, backlog, v6Only, requestClientCertificate, shared); +` + SampleSafeXSSAttack = ` +import 'package:sprintf/sprintf.dart'; +import 'dart:html'; +... + +void RenderHTML(String content) { + var element = new DivElement() + ..textContent = content; + document.body.append(element); +} +` + SampleSafeNoLogSensitive = ` +import 'package:logging/logging.dart'; +... +final _logger = Logger('YourClassName'); + +void ShowUserSensitiveInformation(String identity) { + print("send identity of the user to api"); + _logger.info("send identity of the user to api"); + sentToAPIUserIdentity(identity); +} +... +` + SampleSafeWeakHashingFunctionMd5OrSha1 = ` +import 'dart:convert'; +import 'package:convert/convert.dart'; +import 'package:crypto/crypto.dart' as crypto; + +///Generate sha256 hash +generateSha256(String data) { + var content = new Utf8Encoder().convert(data); + var sha256 = crypto.sha256; + var digest = sha256.convert(content); + return hex.encode(digest.bytes); +} +` + SampleSafeNoUseSelfSignedCertificate = ` +final SecurityContext context = SecurityContext(withTrustedRoots: false); +Socket socket = await Socket.connect(serverIp, port); +socket = await SecureSocket.secure(socket, host: "server" + , context: context, onBadCertificate: (cert) => true); +` + SampleSafeNoUseBiometricsTypeAndroid = ` +try { + authenticated = await auth.CheckTwoFactorAuthenticationAndAuthenticateWithBiometrics( + localizedReason: 'Touch your finger on the sensor to login', + useErrorDialogs: true, + stickyAuth: false + ); +} catch (e) { + print("error using biometric auth: $e"); +} +` + SampleSafeNoListClipboardChanges = ` +_getFromClipboard() async { + var cp = Clipboard + Map result = await cp.getData; + if (result != null) { + return "New content has been updated on Clipboard"; + } + return "Not exists content from Clipboard"; +} + +void sendToAPIToKeepChangesInDatabase() { + try { + String changesFromClipboard = await _getFromClipboard() + if (changesFromClipboard != "" { + // Note this code is safe because only data on sent to API is constants, there not exists vulnerabilities + SaveChangesFromClipboardOnDatabasePost(changesFromClipboard) + } + } on HttpException { + ... + } +} +` + SampleSafeSQLInjection = ` +Database database = await openDatabase(path, version: 1, + onCreate: (Database db, int version) async { + await db.execute('CREATE TABLE Users (id INTEGER PRIMARY KEY, username TEXT, password TEXT);'); +}); + +getCheckIfUserExists(String username) { + try { + List list = await database.rawQuery("SELECT * FROM Users WHERE username = ?;", [username]); + ... + } on Exception { + ... + } +} +` + SampleSafeNoUseNSTemporaryDirectory = ` +let temporaryDirectoryURL = URL(fileURLWithPath: "Some/Other/Path", isDirectory: true) +` + SampleSafeNoUseCipherMode = ` +final encrypter = Encrypter(AES(key, mode: AESMode.cbc)); +` + SampleSafeCorsAllowOriginWildCard = ` +HttpServer.bind('127.0.0.1', 8080).then((server){ + server.listen((HttpRequest request){ + request.uri.queryParameters.forEach((param,val){ + print(param + '-' + val); + }); + + request.response.headers.add("Access-Control-Allow-Origin", "only-my-website.com.br"); + request.response.headers.add("Access-Control-Allow-Methods", "POST,GET,DELETE,PUT,OPTIONS"); + + request.response.statusCode = HttpStatus.OK; + request.response.write("Success!"); + request.response.close(); + }); +}); +` + SampleSafeUsingShellInterpreterWhenExecutingOSCommand = ` +// You can get IP using library or interact with your backend application +var getIPFromLoggedUser => await MyIpPost() ` )