Skip to content

Commit 6bb011a

Browse files
committed
JS: Stop using data/taint as flow labels in TaintedPath
1 parent 0823f6c commit 6bb011a

File tree

2 files changed

+46
-64
lines changed

2 files changed

+46
-64
lines changed

javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,10 +225,10 @@ module TaintTracking {
225225
/**
226226
* A taint propagating data flow edge through persistent storage.
227227
*/
228-
private class StorageTaintStep extends AdditionalTaintStep {
228+
class PersistentStorageTaintStep extends AdditionalTaintStep {
229229
PersistentReadAccess read;
230230

231-
StorageTaintStep() { this = read }
231+
PersistentStorageTaintStep() { this = read }
232232

233233
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
234234
pred = read.getAWrite().getValue() and

javascript/ql/src/semmle/javascript/security/dataflow/TaintedPath.qll

Lines changed: 44 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -98,53 +98,30 @@ module TaintedPath {
9898
not (isNormalized() and isAbsolute())
9999
}
100100
}
101-
102-
/**
103-
* Gets the possible Posix path labels corresponding to `label`.
104-
*
105-
* A posix path label is just mapped to itself, but `data` and `taint` are assumed
106-
* to be fully user-controlled, and thus map to every possible posix path label.
107-
*/
108-
PosixPath toPosixPath(DataFlow::FlowLabel label) {
109-
result = label
110-
or
111-
label instanceof DataFlow::StandardFlowLabel
112-
}
113-
}
114-
115-
/** Gets any flow label. */
116-
private DataFlow::FlowLabel anyLabel() { any() }
117-
118-
/**
119-
* Maps any label to itself, except `data` which is mapped to `taint`.
120-
*/
121-
private predicate preserveLabel(DataFlow::FlowLabel srclabel, DataFlow::FlowLabel dstlabel) {
122-
srclabel != DataFlow::FlowLabel::data() and
123-
dstlabel = srclabel
124-
or
125-
srclabel = DataFlow::FlowLabel::data() and
126-
dstlabel = DataFlow::FlowLabel::taint()
127101
}
128102

129103
/**
130104
* A taint-tracking configuration for reasoning about tainted-path vulnerabilities.
131105
*/
132-
class Configuration extends TaintTracking::Configuration {
106+
class Configuration extends DataFlow::Configuration {
133107
Configuration() { this = "TaintedPath" }
134108

135-
override predicate isSource(DataFlow::Node source) { source instanceof Source }
109+
override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) {
110+
source instanceof Source and
111+
label instanceof Label::PosixPath
112+
}
136113

137114
override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) {
138115
sink instanceof Sink and
139-
label = anyLabel()
116+
label instanceof Label::PosixPath
140117
}
141118

142-
override predicate isSanitizer(DataFlow::Node node) {
143-
super.isSanitizer(node) or
119+
override predicate isBarrier(DataFlow::Node node) {
120+
super.isBarrier(node) or
144121
node instanceof Sanitizer
145122
}
146123

147-
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) {
124+
override predicate isBarrierGuard(DataFlow::BarrierGuardNode guard) {
148125
guard instanceof StartsWithDotDotSanitizer or
149126
guard instanceof StartsWithDirSanitizer or
150127
guard instanceof IsAbsoluteSanitizer or
@@ -168,7 +145,7 @@ module TaintedPath {
168145
or
169146
// Ignore all preliminary sanitization after decoding URI components
170147
srclabel instanceof Label::PosixPath and
171-
dstlabel = DataFlow::FlowLabel::taint() and
148+
dstlabel instanceof Label::PosixPath and
172149
(
173150
any(UriLibraryStep step).step(src, dst)
174151
or
@@ -179,49 +156,54 @@ module TaintedPath {
179156
dst = decode
180157
)
181158
)
182-
}
183-
184-
override predicate isOmittedTaintStep(DataFlow::Node src, DataFlow::Node dst) {
185-
isTaintedPathStep(src, dst, _, _)
159+
or
160+
promiseTaintStep(src, dst) and srclabel = dstlabel
161+
or
162+
any(TaintTracking::PersistentStorageTaintStep st).step(src, dst) and srclabel = dstlabel
163+
or
164+
exists(DataFlow::PropRead read | read = dst |
165+
src = read.getBase() and
166+
read.getPropertyName() != "length" and
167+
srclabel = dstlabel
168+
)
186169
}
187170

188171
/**
189172
* Holds if we should include a step from `src -> dst` with labels `srclabel -> dstlabel`, and the
190173
* standard taint step `src -> dst` should be suppresesd.
191174
*/
192175
predicate isTaintedPathStep(
193-
DataFlow::Node src, DataFlow::Node dst, DataFlow::FlowLabel srclabel,
194-
DataFlow::FlowLabel dstlabel
176+
DataFlow::Node src, DataFlow::Node dst, Label::PosixPath srclabel,
177+
Label::PosixPath dstlabel
195178
) {
196179
// path.normalize() and similar
197180
exists(NormalizingPathCall call |
198181
src = call.getInput() and
199182
dst = call.getOutput() and
200-
dstlabel = Label::toPosixPath(srclabel).toNormalized()
183+
dstlabel = srclabel.toNormalized()
201184
)
202185
or
203186
// path.resolve() and similar
204187
exists(ResolvingPathCall call |
205188
src = call.getInput() and
206189
dst = call.getOutput() and
207-
srclabel = anyLabel() and
208-
dstlabel.(Label::PosixPath).isAbsolute() and
209-
dstlabel.(Label::PosixPath).isNormalized()
190+
dstlabel.isAbsolute() and
191+
dstlabel.isNormalized()
210192
)
211193
or
212194
// path.relative() and similar
213195
exists(NormalizingRelativePathCall call |
214196
src = call.getInput() and
215197
dst = call.getOutput() and
216-
dstlabel.(Label::PosixPath).isRelative() and
217-
dstlabel.(Label::PosixPath).isNormalized()
198+
dstlabel.isRelative() and
199+
dstlabel.isNormalized()
218200
)
219201
or
220202
// path.dirname() and similar
221203
exists(PreservingPathCall call |
222204
src = call.getInput() and
223205
dst = call.getOutput() and
224-
preserveLabel(srclabel, dstlabel)
206+
srclabel = dstlabel
225207
)
226208
or
227209
// path.join()
@@ -233,12 +215,12 @@ module TaintedPath {
233215
(
234216
// If the initial argument is tainted, just normalize it. It can be relative or absolute.
235217
n = 0 and
236-
dstlabel = Label::toPosixPath(srclabel).toNormalized()
218+
dstlabel = srclabel.toNormalized()
237219
or
238220
// For later arguments, the flow label depends on whether the first argument is absolute or relative.
239221
// If in doubt, we assume it is absolute.
240222
n > 0 and
241-
Label::toPosixPath(srclabel).canContainDotDotSlash() and
223+
srclabel.canContainDotDotSlash() and
242224
dstlabel.(Label::PosixPath).isNormalized() and
243225
if isRelative(join.getArgument(0).getStringValue())
244226
then dstlabel.(Label::PosixPath).isRelative()
@@ -252,10 +234,10 @@ module TaintedPath {
252234
|
253235
// use ordinary taint flow for the first operand
254236
n = 0 and
255-
preserveLabel(srclabel, dstlabel)
237+
srclabel = dstlabel
256238
or
257239
n > 0 and
258-
Label::toPosixPath(srclabel).canContainDotDotSlash() and
240+
srclabel.canContainDotDotSlash() and
259241
dstlabel.(Label::PosixPath).isNonNormalized() and // The ../ is no longer at the beginning of the string.
260242
(
261243
if isRelative(StringConcatenation::getOperand(operator, 0).getStringValue())
@@ -414,22 +396,22 @@ module TaintedPath {
414396
*
415397
* This is relevant for paths that are known to be normalized.
416398
*/
417-
class StartsWithDotDotSanitizer extends TaintTracking::LabeledSanitizerGuardNode {
399+
class StartsWithDotDotSanitizer extends DataFlow::LabeledBarrierGuardNode {
418400
StringOps::StartsWith startsWith;
419401

420402
StartsWithDotDotSanitizer() {
421403
this = startsWith and
422404
isDotDotSlashPrefix(startsWith.getSubstring())
423405
}
424406

425-
override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) {
407+
override predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel label) {
426408
// Sanitize in the false case for:
427409
// .startsWith(".")
428410
// .startsWith("..")
429411
// .startsWith("../")
430412
outcome = startsWith.getPolarity().booleanNot() and
431413
e = startsWith.getBaseString().asExpr() and
432-
exists(Label::PosixPath posixPath | posixPath = Label::toPosixPath(label) |
414+
exists(Label::PosixPath posixPath | posixPath = label |
433415
posixPath.isNormalized() and
434416
posixPath.isRelative()
435417
)
@@ -440,7 +422,7 @@ module TaintedPath {
440422
* A check of form `x.startsWith(dir)` that sanitizes normalized absolute paths, since it is then
441423
* known to be in a subdirectory of `dir`.
442424
*/
443-
class StartsWithDirSanitizer extends TaintTracking::LabeledSanitizerGuardNode {
425+
class StartsWithDirSanitizer extends DataFlow::LabeledBarrierGuardNode {
444426
StringOps::StartsWith startsWith;
445427

446428
StartsWithDirSanitizer() {
@@ -450,10 +432,10 @@ module TaintedPath {
450432
not startsWith.getSubstring().asExpr().getStringValue() = "/"
451433
}
452434

453-
override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) {
435+
override predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel label) {
454436
outcome = startsWith.getPolarity() and
455437
e = startsWith.getBaseString().asExpr() and
456-
exists(Label::PosixPath posixPath | posixPath = Label::toPosixPath(label) |
438+
exists(Label::PosixPath posixPath | posixPath = label |
457439
posixPath.isAbsolute() and
458440
posixPath.isNormalized()
459441
)
@@ -464,7 +446,7 @@ module TaintedPath {
464446
* A call to `path.isAbsolute` as a sanitizer for relative paths in true branch,
465447
* and a sanitizer for absolute paths in the false branch.
466448
*/
467-
class IsAbsoluteSanitizer extends TaintTracking::LabeledSanitizerGuardNode {
449+
class IsAbsoluteSanitizer extends DataFlow::LabeledBarrierGuardNode {
468450
DataFlow::Node operand;
469451

470452
boolean polarity;
@@ -487,9 +469,9 @@ module TaintedPath {
487469
) // !x.startsWith("/home") does not guarantee that x is not absolute
488470
}
489471

490-
override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) {
472+
override predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel label) {
491473
e = operand.asExpr() and
492-
exists(Label::PosixPath posixPath | posixPath = Label::toPosixPath(label) |
474+
exists(Label::PosixPath posixPath | posixPath = label |
493475
outcome = polarity and posixPath.isRelative()
494476
or
495477
negatable = true and
@@ -502,18 +484,18 @@ module TaintedPath {
502484
/**
503485
* An expression of form `x.includes("..")` or similar.
504486
*/
505-
class ContainsDotDotSanitizer extends TaintTracking::LabeledSanitizerGuardNode {
487+
class ContainsDotDotSanitizer extends DataFlow::LabeledBarrierGuardNode {
506488
StringOps::Includes contains;
507489

508490
ContainsDotDotSanitizer() {
509491
this = contains and
510492
isDotDotSlashPrefix(contains.getSubstring())
511493
}
512494

513-
override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) {
495+
override predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel label) {
514496
e = contains.getBaseString().asExpr() and
515497
outcome = contains.getPolarity().booleanNot() and
516-
Label::toPosixPath(label).canContainDotDotSlash() // can still be bypassed by normalized absolute path
498+
label.(Label::PosixPath).canContainDotDotSlash() // can still be bypassed by normalized absolute path
517499
}
518500
}
519501

0 commit comments

Comments
 (0)