Skip to content

Commit 741735c

Browse files
author
Max Schaefer
committed
Port changes to JavaScript.
1 parent 3939167 commit 741735c

File tree

5 files changed

+83
-36
lines changed

5 files changed

+83
-36
lines changed

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

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ private module AsmCrypto {
5151
private class Apply extends CryptographicOperation::Range instanceof DataFlow::CallNode {
5252
DataFlow::Node input;
5353
CryptographicAlgorithm algorithm; // non-functional
54+
DataFlow::PropRead algorithmSelection;
5455
private string algorithmName;
5556
private string methodName;
5657

@@ -68,11 +69,14 @@ private module AsmCrypto {
6869
exists(DataFlow::SourceNode asmCrypto |
6970
asmCrypto = DataFlow::globalVarRef("asmCrypto") and
7071
algorithm.matchesName(algorithmName) and
71-
this = asmCrypto.getAPropertyRead(algorithmName).getAMemberCall(methodName) and
72+
algorithmSelection = asmCrypto.getAPropertyRead(algorithmName) and
73+
this = algorithmSelection.getAMemberCall(methodName) and
7274
input = this.getArgument(0)
7375
)
7476
}
7577

78+
override DataFlow::Node getInitialization() { result = algorithmSelection }
79+
7680
override DataFlow::Node getAnInput() { result = input }
7781

7882
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
@@ -103,6 +107,7 @@ private module BrowserIdCrypto {
103107

104108
private class Apply extends CryptographicOperation::Range instanceof DataFlow::MethodCallNode {
105109
CryptographicAlgorithm algorithm; // non-functional
110+
DataFlow::CallNode keygen;
106111

107112
Apply() {
108113
/*
@@ -122,8 +127,7 @@ private module BrowserIdCrypto {
122127
*/
123128

124129
exists(
125-
DataFlow::SourceNode mod, DataFlow::Node algorithmNameNode, DataFlow::CallNode keygen,
126-
DataFlow::FunctionNode callback
130+
DataFlow::SourceNode mod, DataFlow::Node algorithmNameNode, DataFlow::FunctionNode callback
127131
|
128132
mod = DataFlow::moduleImport("browserid-crypto") and
129133
keygen = mod.getAMemberCall("generateKeypair") and
@@ -134,6 +138,8 @@ private module BrowserIdCrypto {
134138
)
135139
}
136140

141+
override DataFlow::Node getInitialization() { result = keygen }
142+
137143
override DataFlow::Node getAnInput() { result = super.getArgument(0) }
138144

139145
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
@@ -239,6 +245,8 @@ private module NodeJSCrypto {
239245

240246
Apply() { this = instantiation.getAMethodCall(any(string m | m = "update" or m = "write")) }
241247

248+
override DataFlow::Node getInitialization() { result = instantiation }
249+
242250
override DataFlow::Node getAnInput() { result = super.getArgument(0) }
243251

244252
override CryptographicAlgorithm getAlgorithm() { result = instantiation.getAlgorithm() }
@@ -324,7 +332,9 @@ private module CryptoJS {
324332
)
325333
}
326334

327-
private API::CallNode getEncryptionApplication(API::Node input, CryptographicAlgorithm algorithm) {
335+
private API::CallNode getEncryptionApplication(
336+
API::Node input, API::Node algorithmNode, CryptographicAlgorithm algorithm
337+
) {
328338
/*
329339
* ```
330340
* var CryptoJS = require("crypto-js");
@@ -338,11 +348,12 @@ private module CryptoJS {
338348
* Also matches where `CryptoJS.<algorithmName>` has been replaced by `require("crypto-js/<algorithmName>")`
339349
*/
340350

341-
result = getAlgorithmNode(algorithm).getMember("encrypt").getACall() and
351+
algorithmNode = getAlgorithmNode(algorithm) and
352+
result = algorithmNode.getMember("encrypt").getACall() and
342353
input = result.getParameter(0)
343354
}
344355

345-
private API::CallNode getDirectApplication(API::Node input, CryptographicAlgorithm algorithm) {
356+
private API::CallNode getDirectApplication(API::Node input, API::Node algorithmNode, CryptographicAlgorithm algorithm) {
346357
/*
347358
* ```
348359
* var CryptoJS = require("crypto-js");
@@ -356,8 +367,8 @@ private module CryptoJS {
356367
* An `Hmac`-prefix of <algorithmName> is ignored.
357368
* Also matches where `CryptoJS.<algorithmName>` has been replaced by `require("crypto-js/<algorithmName>")`
358369
*/
359-
360-
result = getAlgorithmNode(algorithm).getACall() and
370+
algorithmNode = getAlgorithmNode(algorithm) and
371+
result = algorithmNode.getACall() and
361372
input = result.getParameter(0)
362373
}
363374

@@ -389,18 +400,23 @@ private module CryptoJS {
389400
private class Apply extends CryptographicOperation::Range instanceof API::CallNode {
390401
API::Node input;
391402
CryptographicAlgorithm algorithm; // non-functional
403+
DataFlow::Node instantiation;
392404

393405
Apply() {
394-
this = getEncryptionApplication(input, algorithm)
395-
or
396-
this = getDirectApplication(input, algorithm)
397-
or
398-
exists(InstantiatedAlgorithm instantiation |
399-
this = getUpdatedApplication(input, instantiation) and
400-
algorithm = instantiation.getAlgorithm()
406+
exists(API::Node algorithmNode |
407+
this = getEncryptionApplication(input, algorithmNode, algorithm)
408+
or
409+
this = getDirectApplication(input, algorithmNode, algorithm)
410+
|
411+
instantiation = algorithmNode.asSource()
401412
)
413+
or
414+
this = getUpdatedApplication(input, instantiation) and
415+
algorithm = instantiation.(InstantiatedAlgorithm).getAlgorithm()
402416
}
403417

418+
override DataFlow::Node getInitialization() { result = instantiation }
419+
404420
override DataFlow::Node getAnInput() { result = input.asSink() }
405421

406422
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
@@ -504,6 +520,8 @@ private module TweetNaCl {
504520
)
505521
}
506522

523+
override DataFlow::Node getInitialization() { result = this }
524+
507525
override DataFlow::Node getAnInput() { result = input }
508526

509527
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
@@ -539,6 +557,7 @@ private module HashJs {
539557
private class Apply extends CryptographicOperation::Range instanceof DataFlow::CallNode {
540558
DataFlow::Node input;
541559
CryptographicAlgorithm algorithm; // non-functional
560+
DataFlow::CallNode init;
542561

543562
Apply() {
544563
/*
@@ -553,11 +572,13 @@ private module HashJs {
553572
* ```
554573
* Also matches where `hash.<algorithmName>()` has been replaced by a more specific require a la `require("hash.js/lib/hash/sha/512")`
555574
*/
556-
557-
this = getAlgorithmNode(algorithm).getAMemberCall("update") and
575+
init = getAlgorithmNode(algorithm) and
576+
this = init.getAMemberCall("update") and
558577
input = super.getArgument(0)
559578
}
560579

580+
override DataFlow::Node getInitialization() { result = init }
581+
561582
override DataFlow::Node getAnInput() { result = input }
562583

563584
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
@@ -653,6 +674,8 @@ private module Forge {
653674
algorithm = cipher.getAlgorithm()
654675
}
655676

677+
override DataFlow::Node getInitialization() { result = cipher }
678+
656679
override DataFlow::Node getAnInput() { result = input }
657680

658681
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
@@ -715,6 +738,8 @@ private module Md5 {
715738
super.getArgument(0) = input
716739
}
717740

741+
override DataFlow::Node getInitialization() { result = this }
742+
718743
override DataFlow::Node getAnInput() { result = input }
719744

720745
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
@@ -731,17 +756,21 @@ private module Bcrypt {
731756
private class Apply extends CryptographicOperation::Range instanceof DataFlow::CallNode {
732757
DataFlow::Node input;
733758
CryptographicAlgorithm algorithm;
759+
API::Node init;
734760

735761
Apply() {
736762
// `require("bcrypt").hash(password);` with minor naming variations
737763
algorithm.matchesName("BCRYPT") and
764+
init = API::moduleImport(["bcrypt", "bcryptjs", "bcrypt-nodejs"]) and
738765
this =
739-
API::moduleImport(["bcrypt", "bcryptjs", "bcrypt-nodejs"])
766+
init
740767
.getMember(["hash", "hashSync"])
741768
.getACall() and
742769
super.getArgument(0) = input
743770
}
744771

772+
override DataFlow::Node getInitialization() { result = init.asSource() }
773+
745774
override DataFlow::Node getAnInput() { result = input }
746775

747776
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
@@ -769,6 +798,8 @@ private module Hasha {
769798
)
770799
}
771800

801+
override DataFlow::Node getInitialization() { result = this }
802+
772803
override DataFlow::Node getAnInput() { result = input }
773804

774805
override CryptographicAlgorithm getAlgorithm() { result = algorithm }

javascript/ql/lib/semmle/javascript/internal/ConceptsShared.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ module Cryptography {
4040
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
4141
CryptographicAlgorithm getAlgorithm() { result = super.getAlgorithm() }
4242

43+
/** Gets the data-flow node where the cryptographic algorithm used in this operation is configured. */
44+
DataFlow::Node getInitialization() { result = super.getInitialization() }
45+
4346
/** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */
4447
DataFlow::Node getAnInput() { result = super.getAnInput() }
4548

@@ -65,6 +68,9 @@ module Cryptography {
6568
* extend `CryptographicOperation` instead.
6669
*/
6770
abstract class Range extends DataFlow::Node {
71+
/** Gets the data-flow node where the cryptographic algorithm used in this operation is configured. */
72+
abstract DataFlow::Node getInitialization();
73+
6874
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
6975
abstract CryptographicAlgorithm getAlgorithm();
7076

javascript/ql/lib/semmle/javascript/security/dataflow/BrokenCryptoAlgorithmCustomizations.qll

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ module BrokenCryptoAlgorithm {
1919
/**
2020
* A data flow sink for sensitive information in broken or weak cryptographic algorithms.
2121
*/
22-
abstract class Sink extends DataFlow::Node { }
22+
abstract class Sink extends DataFlow::Node {
23+
/** Gets the data-flow node where the cryptographic algorithm used in this operation is configured. */
24+
abstract DataFlow::Node getInitialization();
25+
}
2326

2427
/**
2528
* A sanitizer for sensitive information in broken or weak cryptographic algorithms.
@@ -38,15 +41,17 @@ module BrokenCryptoAlgorithm {
3841
* An expression used by a broken or weak cryptographic algorithm.
3942
*/
4043
class WeakCryptographicOperationSink extends Sink {
44+
CryptographicOperation application;
45+
4146
WeakCryptographicOperationSink() {
42-
exists(CryptographicOperation application |
43-
(
44-
application.getAlgorithm().isWeak()
45-
or
46-
application.getBlockMode().isWeak()
47-
) and
48-
this = application.getAnInput()
49-
)
47+
(
48+
application.getAlgorithm().isWeak()
49+
or
50+
application.getBlockMode().isWeak()
51+
) and
52+
this = application.getAnInput()
5053
}
54+
55+
override DataFlow::Node getInitialization() { result = application.getInitialization() }
5156
}
5257
}

javascript/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,14 @@ import semmle.javascript.security.dataflow.BrokenCryptoAlgorithmQuery
1616
import semmle.javascript.security.SensitiveActions
1717
import DataFlow::PathGraph
1818

19-
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
19+
from
20+
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Source sourceNode,
21+
Sink sinkNode
2022
where
2123
cfg.hasFlowPath(source, sink) and
22-
not source.getNode() instanceof CleartextPasswordExpr // flagged by js/insufficient-password-hash
23-
select sink.getNode(), source, sink, "A broken or weak cryptographic algorithm depends on $@.",
24-
source.getNode(), "sensitive data from " + source.getNode().(Source).describe()
24+
sourceNode = source.getNode() and
25+
sinkNode = sink.getNode() and
26+
not sourceNode instanceof CleartextPasswordExpr // flagged by js/insufficient-password-hash
27+
select sinkNode, source, sink,
28+
"A broken or weak cryptographic algorithm (configured $@) depends on $@.",
29+
sinkNode.getInitialization(), "here", sourceNode, "sensitive data from " + sourceNode.describe()

javascript/ql/test/query-tests/Security/CWE-327/BrokenCryptoAlgorithm.expected

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ edges
2626
| tst.js:19:17:19:24 | password | tst.js:19:17:19:24 | password |
2727
| tst.js:22:21:22:30 | secretText | tst.js:22:21:22:30 | secretText |
2828
#select
29-
| tst.js:11:17:11:26 | secretText | tst.js:3:18:3:24 | trusted | tst.js:11:17:11:26 | secretText | A broken or weak cryptographic algorithm depends on $@. | tst.js:3:18:3:24 | trusted | sensitive data from an access to trusted |
30-
| tst.js:11:17:11:26 | secretText | tst.js:11:17:11:26 | secretText | tst.js:11:17:11:26 | secretText | A broken or weak cryptographic algorithm depends on $@. | tst.js:11:17:11:26 | secretText | sensitive data from an access to secretText |
31-
| tst.js:17:17:17:25 | o.trusted | tst.js:17:17:17:25 | o.trusted | tst.js:17:17:17:25 | o.trusted | A broken or weak cryptographic algorithm depends on $@. | tst.js:17:17:17:25 | o.trusted | sensitive data from an access to trusted |
32-
| tst.js:22:21:22:30 | secretText | tst.js:3:18:3:24 | trusted | tst.js:22:21:22:30 | secretText | A broken or weak cryptographic algorithm depends on $@. | tst.js:3:18:3:24 | trusted | sensitive data from an access to trusted |
33-
| tst.js:22:21:22:30 | secretText | tst.js:22:21:22:30 | secretText | tst.js:22:21:22:30 | secretText | A broken or weak cryptographic algorithm depends on $@. | tst.js:22:21:22:30 | secretText | sensitive data from an access to secretText |
29+
| tst.js:11:17:11:26 | secretText | tst.js:3:18:3:24 | trusted | tst.js:11:17:11:26 | secretText | A broken or weak cryptographic algorithm (configured $@) depends on $@. | tst.js:5:19:5:49 | crypto. ... ', key) | here | tst.js:3:18:3:24 | trusted | sensitive data from an access to trusted |
30+
| tst.js:11:17:11:26 | secretText | tst.js:11:17:11:26 | secretText | tst.js:11:17:11:26 | secretText | A broken or weak cryptographic algorithm (configured $@) depends on $@. | tst.js:5:19:5:49 | crypto. ... ', key) | here | tst.js:11:17:11:26 | secretText | sensitive data from an access to secretText |
31+
| tst.js:17:17:17:25 | o.trusted | tst.js:17:17:17:25 | o.trusted | tst.js:17:17:17:25 | o.trusted | A broken or weak cryptographic algorithm (configured $@) depends on $@. | tst.js:5:19:5:49 | crypto. ... ', key) | here | tst.js:17:17:17:25 | o.trusted | sensitive data from an access to trusted |
32+
| tst.js:22:21:22:30 | secretText | tst.js:3:18:3:24 | trusted | tst.js:22:21:22:30 | secretText | A broken or weak cryptographic algorithm (configured $@) depends on $@. | tst.js:21:22:21:60 | crypto. ... ', key) | here | tst.js:3:18:3:24 | trusted | sensitive data from an access to trusted |
33+
| tst.js:22:21:22:30 | secretText | tst.js:22:21:22:30 | secretText | tst.js:22:21:22:30 | secretText | A broken or weak cryptographic algorithm (configured $@) depends on $@. | tst.js:21:22:21:60 | crypto. ... ', key) | here | tst.js:22:21:22:30 | secretText | sensitive data from an access to secretText |

0 commit comments

Comments
 (0)