Skip to content

Commit 53de9ae

Browse files
authored
Merge pull request github#12729 from asgerf/js/crypto-modernize
JS: Modernize crypto libraries
2 parents 6331c37 + 64cf27a commit 53de9ae

File tree

1 file changed

+67
-96
lines changed

1 file changed

+67
-96
lines changed

javascript/ql/lib/semmle/javascript/frameworks/CryptoLibraries.qll

Lines changed: 67 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ private module BrowserIdCrypto {
149149
* A model of the Node.js builtin crypto library.
150150
*/
151151
private module NodeJSCrypto {
152-
private class InstantiatedAlgorithm extends DataFlow::CallNode {
152+
private class InstantiatedAlgorithm extends API::CallNode {
153153
private string algorithmName;
154154

155155
InstantiatedAlgorithm() {
@@ -166,11 +166,11 @@ private module NodeJSCrypto {
166166
* Also matches `createHash`, `createHmac`, `createSign` instead of `createCipher`.
167167
*/
168168

169-
exists(DataFlow::SourceNode mod |
170-
mod = DataFlow::moduleImport("crypto") and
171-
this = mod.getAMemberCall("create" + ["Hash", "Hmac", "Sign", "Cipher"]) and
172-
algorithmName = this.getArgument(0).getStringValue()
173-
)
169+
this =
170+
API::moduleImport("crypto")
171+
.getMember("create" + ["Hash", "Hmac", "Sign", "Cipher"])
172+
.getACall() and
173+
algorithmName = this.getArgument(0).getStringValue()
174174
}
175175

176176
CryptographicAlgorithm getAlgorithm() { result.matchesName(algorithmName) }
@@ -197,13 +197,12 @@ private module NodeJSCrypto {
197197
// crypto.generateKeyPair(type, options, callback)
198198
// crypto.generateKeyPairSync(type, options)
199199
// crypto.generateKeySync(type, options)
200-
exists(DataFlow::SourceNode mod, string keyType |
200+
exists(string keyType |
201201
keyType = "Key" and symmetric = true
202202
or
203203
keyType = "KeyPair" and symmetric = false
204204
|
205-
mod = DataFlow::moduleImport("crypto") and
206-
this = mod.getAMemberCall("generate" + keyType + ["", "Sync"])
205+
this = API::moduleImport("crypto").getMember("generate" + keyType + ["", "Sync"]).getACall()
207206
)
208207
}
209208

@@ -249,17 +248,15 @@ private module NodeJSCrypto {
249248

250249
private class Key extends CryptographicKey {
251250
Key() {
252-
exists(InstantiatedAlgorithm instantiation, string name |
253-
name = "setPrivateKey" or
254-
name = "sign"
255-
|
256-
this = instantiation.getAMethodCall(name).getArgument(0)
257-
)
251+
this =
252+
any(InstantiatedAlgorithm i)
253+
.getReturn()
254+
.getMember(["setPrivateKey", "sign"])
255+
.getParameter(0)
256+
.asSink()
258257
or
259-
exists(DataFlow::SourceNode mod, string name, DataFlow::InvokeNode call, int index |
260-
mod = DataFlow::moduleImport("crypto") and
261-
call = mod.getAMemberCall(name) and
262-
this = call.getArgument(index)
258+
exists(string name, int index |
259+
this = API::moduleImport("crypto").getMember(name).getACall().getArgument(index)
263260
|
264261
index = 0 and
265262
(name = "privateDecrypt" or name = "privateEncrypt")
@@ -291,13 +288,13 @@ private module CryptoJS {
291288
* ```
292289
*/
293290

294-
exists(DataFlow::SourceNode mod, DataFlow::PropRead propRead |
295-
mod = DataFlow::moduleImport("crypto-js") and
296-
propRead = mod.getAPropertyRead("algo").getAPropertyRead() and
297-
this = propRead.getAMemberCall("create") and
298-
algorithmName = propRead.getPropertyName() and
299-
not isStrongPasswordHashingAlgorithm(algorithmName)
300-
)
291+
this =
292+
API::moduleImport("crypto-js")
293+
.getMember("algo")
294+
.getMember(algorithmName)
295+
.getMember("create")
296+
.getACall() and
297+
not isStrongPasswordHashingAlgorithm(algorithmName)
301298
}
302299

303300
CryptographicAlgorithm getAlgorithm() { result.matchesName(algorithmName) }
@@ -321,10 +318,7 @@ private module CryptoJS {
321318
*/
322319
private API::Node getAlgorithmNode(CryptographicAlgorithm algorithm) {
323320
exists(string algorithmName | algorithm.matchesName(algorithmName) |
324-
exists(API::Node mod | mod = API::moduleImport("crypto-js") |
325-
result = mod.getMember(algorithmName) or
326-
result = mod.getMember("Hmac" + algorithmName) // they prefix Hmac
327-
)
321+
result = API::moduleImport("crypto-js").getMember([algorithmName, "Hmac" + algorithmName])
328322
or
329323
result = API::moduleImport("crypto-js/" + algorithmName)
330324
)
@@ -500,13 +494,12 @@ private module TweetNaCl {
500494
* Also matches the "hash" method name, and the "nacl-fast" module.
501495
*/
502496

503-
exists(DataFlow::SourceNode mod, string name |
497+
exists(string name |
504498
name = "hash" and algorithm.matchesName("SHA512")
505499
or
506500
name = "sign" and algorithm.matchesName("ed25519")
507501
|
508-
(mod = DataFlow::moduleImport("nacl") or mod = DataFlow::moduleImport("nacl-fast")) and
509-
this = mod.getAMemberCall(name) and
502+
this = API::moduleImport(["nacl", "nacl-fast"]).getMember(name).getACall() and
510503
super.getArgument(0) = input
511504
)
512505
}
@@ -532,17 +525,13 @@ private module HashJs {
532525
*/
533526
private DataFlow::CallNode getAlgorithmNode(CryptographicAlgorithm algorithm) {
534527
exists(string algorithmName | algorithm.matchesName(algorithmName) |
535-
result = DataFlow::moduleMember("hash.js", algorithmName).getACall()
528+
result = API::moduleImport("hash.js").getMember(algorithmName).getACall()
536529
or
537-
exists(DataFlow::SourceNode mod |
538-
mod = DataFlow::moduleImport("hash.js/lib/hash/" + algorithmName)
539-
or
540-
exists(string size |
541-
mod = DataFlow::moduleImport("hash.js/lib/hash/sha/" + size) and
542-
algorithmName = "SHA" + size
543-
)
544-
|
545-
result = mod.getACall()
530+
result = API::moduleImport("hash.js/lib/hash/" + algorithmName).getACall()
531+
or
532+
exists(string size |
533+
result = API::moduleImport("hash.js/lib/hash/sha/" + size).getACall() and
534+
algorithmName = "SHA" + size
546535
)
547536
)
548537
}
@@ -582,10 +571,7 @@ private module HashJs {
582571
* A model of the forge library.
583572
*/
584573
private module Forge {
585-
private DataFlow::SourceNode getAnImportNode() {
586-
result = DataFlow::moduleImport("forge") or
587-
result = DataFlow::moduleImport("node-forge")
588-
}
574+
private API::Node getAnImportNode() { result = API::moduleImport(["forge", "node-forge"]) }
589575

590576
abstract private class Cipher extends DataFlow::CallNode {
591577
abstract CryptographicAlgorithm getAlgorithm();
@@ -597,14 +583,14 @@ private module Forge {
597583
private string blockModeString;
598584

599585
KeyCipher() {
600-
exists(DataFlow::SourceNode mod, string algorithmName |
601-
mod = getAnImportNode() and
602-
algorithm.matchesName(algorithmName)
603-
|
604-
exists(string createName, string cipherName, string cipherPrefix |
586+
exists(string algorithmName | algorithm.matchesName(algorithmName) |
587+
exists(string cipherName, string cipherPrefix |
605588
// `require('forge').cipher.createCipher("3DES-CBC").update("secret", "key");`
606-
(createName = "createCipher" or createName = "createDecipher") and
607-
this = mod.getAPropertyRead("cipher").getAMemberCall(createName) and
589+
this =
590+
getAnImportNode()
591+
.getMember("cipher")
592+
.getMember(["createCipher", "createDecipher"])
593+
.getACall() and
608594
this.getArgument(0).mayHaveStringValue(cipherName) and
609595
cipherName = cipherPrefix + "-" + blockModeString and
610596
blockModeString = ["CBC", "CFB", "CTR", "ECB", "GCM", "OFB"] and
@@ -613,13 +599,13 @@ private module Forge {
613599
)
614600
or
615601
// `require("forge").rc2.createEncryptionCipher("key").update("secret");`
616-
exists(string createName |
617-
createName = "createEncryptionCipher" or createName = "createDecryptionCipher"
618-
|
619-
this = mod.getAPropertyRead(algorithmName).getAMemberCall(createName) and
620-
key = this.getArgument(0) and
621-
blockModeString = algorithmName
622-
)
602+
this =
603+
getAnImportNode()
604+
.getMember(algorithmName)
605+
.getMember(["createEncryptionCipher", "createDecryptionCipher"])
606+
.getACall() and
607+
key = this.getArgument(0) and
608+
blockModeString = algorithmName
623609
)
624610
}
625611

@@ -640,10 +626,7 @@ private module Forge {
640626
exists(string algorithmName | algorithm.matchesName(algorithmName) |
641627
// require("forge").md.md5.create().update('The quick brown fox jumps over the lazy dog');
642628
this =
643-
getAnImportNode()
644-
.getAPropertyRead("md")
645-
.getAPropertyRead(algorithmName)
646-
.getAMemberCall("create")
629+
getAnImportNode().getMember("md").getMember(algorithmName).getMember("create").getACall()
647630
)
648631
}
649632

@@ -679,15 +662,17 @@ private module Forge {
679662
// var cipher = forge.rc2.createEncryptionCipher(key, 128);
680663
this =
681664
getAnImportNode()
682-
.getAPropertyRead(any(string s | algorithm.matchesName(s)))
683-
.getAMemberCall("createEncryptionCipher")
665+
.getMember(any(string s | algorithm.matchesName(s)))
666+
.getMember("createEncryptionCipher")
667+
.getACall()
684668
or
685669
// var key = forge.random.getBytesSync(16);
686670
// var cipher = forge.cipher.createCipher('AES-CBC', key);
687671
this =
688672
getAnImportNode()
689-
.getAPropertyRead("cipher")
690-
.getAMemberCall(["createCipher", "createDecipher"]) and
673+
.getMember("cipher")
674+
.getMember(["createCipher", "createDecipher"])
675+
.getACall() and
691676
algorithm.matchesName(this.getArgument(0).getStringValue())
692677
}
693678

@@ -716,12 +701,9 @@ private module Md5 {
716701

717702
Apply() {
718703
// `require("md5")("message");`
719-
exists(DataFlow::SourceNode mod |
720-
mod = DataFlow::moduleImport("md5") and
721-
algorithm.matchesName("MD5") and
722-
this = mod.getACall() and
723-
super.getArgument(0) = input
724-
)
704+
algorithm.matchesName("MD5") and
705+
this = API::moduleImport("md5").getACall() and
706+
super.getArgument(0) = input
725707
}
726708

727709
override DataFlow::Node getAnInput() { result = input }
@@ -743,21 +725,12 @@ private module Bcrypt {
743725

744726
Apply() {
745727
// `require("bcrypt").hash(password);` with minor naming variations
746-
exists(DataFlow::SourceNode mod, string moduleName, string methodName |
747-
algorithm.matchesName("BCRYPT") and
748-
(
749-
moduleName = "bcrypt" or
750-
moduleName = "bcryptjs" or
751-
moduleName = "bcrypt-nodejs"
752-
) and
753-
(
754-
methodName = "hash" or
755-
methodName = "hashSync"
756-
) and
757-
mod = DataFlow::moduleImport(moduleName) and
758-
this = mod.getAMemberCall(methodName) and
759-
super.getArgument(0) = input
760-
)
728+
algorithm.matchesName("BCRYPT") and
729+
this =
730+
API::moduleImport(["bcrypt", "bcryptjs", "bcrypt-nodejs"])
731+
.getMember(["hash", "hashSync"])
732+
.getACall() and
733+
super.getArgument(0) = input
761734
}
762735

763736
override DataFlow::Node getAnInput() { result = input }
@@ -779,13 +752,11 @@ private module Hasha {
779752

780753
Apply() {
781754
// `require('hasha')('unicorn', { algorithm: "md5" });`
782-
exists(DataFlow::SourceNode mod, string algorithmName, DataFlow::Node algorithmNameNode |
783-
mod = DataFlow::moduleImport("hasha") and
784-
this = mod.getACall() and
755+
exists(string algorithmName |
756+
this = API::moduleImport("hasha").getACall() and
785757
super.getArgument(0) = input and
786758
algorithm.matchesName(algorithmName) and
787-
super.getOptionArgument(1, "algorithm") = algorithmNameNode and
788-
algorithmNameNode.mayHaveStringValue(algorithmName)
759+
super.getOptionArgument(1, "algorithm").mayHaveStringValue(algorithmName)
789760
)
790761
}
791762

@@ -803,7 +774,7 @@ private module Hasha {
803774
*/
804775
private module ExpressJwt {
805776
private class Key extends CryptographicKey {
806-
Key() { this = DataFlow::moduleMember("express-jwt", "sign").getACall().getArgument(1) }
777+
Key() { this = API::moduleImport("express-jwt").getMember("sign").getACall().getArgument(1) }
807778
}
808779
}
809780

0 commit comments

Comments
 (0)