-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathAdvancedFormatting.qll
More file actions
131 lines (112 loc) · 4.84 KB
/
Copy pathAdvancedFormatting.qll
File metadata and controls
131 lines (112 loc) · 4.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import python
private import LegacyPointsTo
/** A string constant that looks like it may be used in string formatting operations. */
class PossibleAdvancedFormatString extends StringLiteral {
PossibleAdvancedFormatString() { this.getText().matches("%{%}%") }
private predicate field(int start, int end) {
brace_pair(this, start, end) and
this.getText().substring(start, end) != "{{}}"
}
/** Gets the number of the formatting field at [start, end) */
int getFieldNumber(int start, int end) {
result = this.fieldId(start, end).toInt()
or
this.implicitlyNumberedField(start, end) and
result = count(int s | this.implicitlyNumberedField(s, _) and s < start)
}
/** Gets the text of the formatting field at [start, end) */
string getField(int start, int end) {
this.field(start, end) and
result = this.getText().substring(start, end)
}
private string fieldId(int start, int end) {
this.field(start, end) and
(
result = this.getText().substring(start, end).regexpCapture("\\{([^!:.\\[]+)[!:.\\[].*", 1)
or
result = this.getText().substring(start + 1, end - 1) and result.regexpMatch("[^!:.\\[]+")
)
}
/** Gets the name of the formatting field at [start, end) */
string getFieldName(int start, int end) {
result = this.fieldId(start, end) and
not exists(this.getFieldNumber(start, end))
}
private predicate implicitlyNumberedField(int start, int end) {
this.field(start, end) and
exists(string c | start + 1 = this.getText().indexOf(c) | c in ["}", ":", "!", "."])
}
/** Whether this format string has implicitly numbered fields */
predicate isImplicitlyNumbered() { this.implicitlyNumberedField(_, _) }
/** Whether this format string has explicitly numbered fields */
predicate isExplicitlyNumbered() { exists(this.fieldId(_, _).toInt()) }
}
/** Holds if the formatting string `fmt` contains a sequence of braces `{` of length `len`, beginning at index `index`. */
predicate brace_sequence(PossibleAdvancedFormatString fmt, int index, int len) {
exists(string text | text = fmt.getText() |
text.charAt(index) = "{" and not text.charAt(index - 1) = "{" and len = 1
or
text.charAt(index) = "{" and
text.charAt(index - 1) = "{" and
brace_sequence(fmt, index - 1, len - 1)
)
}
/** Holds if index `index` in the format string `fmt` contains an escaped brace `{`. */
predicate escaped_brace(PossibleAdvancedFormatString fmt, int index) {
exists(int len | brace_sequence(fmt, index, len) | len % 2 = 0)
}
/** Holds if index `index` in the format string `fmt` contains a left brace `{` that acts as an escape character. */
predicate escaping_brace(PossibleAdvancedFormatString fmt, int index) {
escaped_brace(fmt, index + 1)
}
private predicate inner_brace_pair(PossibleAdvancedFormatString fmt, int start, int end) {
not escaping_brace(fmt, start) and
not escaped_brace(fmt, start) and
fmt.getText().charAt(start) = "{" and
exists(string pair |
pair = fmt.getText().suffix(start).regexpCapture("(?s)(\\{([^{}]|\\{\\{)*+\\}).*", 1)
|
end = start + pair.length()
)
}
private predicate brace_pair(PossibleAdvancedFormatString fmt, int start, int end) {
inner_brace_pair(fmt, start, end)
or
not escaping_brace(fmt, start) and
not escaped_brace(fmt, start) and
exists(string prefix, string postfix, int innerstart, int innerend |
brace_pair(fmt, innerstart, innerend) and
prefix = fmt.getText().regexpFind("\\{([^{}]|\\{\\{)+\\{", _, start) and
innerstart = start + prefix.length() - 1 and
postfix = fmt.getText().regexpFind("\\}([^{}]|\\}\\})*\\}", _, innerend - 1) and
end = innerend + postfix.length() - 1
)
}
private predicate advanced_format_call(Call format_expr, PossibleAdvancedFormatString fmt, int args) {
exists(CallNode call, ControlFlowNode fmtCfg |
call.getNode() = format_expr and fmtCfg.getNode() = fmt
|
call.getFunction().(ControlFlowNodeWithPointsTo).pointsTo(Value::named("format")) and
call.getArg(0).(ControlFlowNodeWithPointsTo).pointsTo(_, fmtCfg) and
args = count(format_expr.getAnArg()) - 1
or
call.getFunction()
.(AttrNode)
.getObject("format")
.(ControlFlowNodeWithPointsTo)
.pointsTo(_, fmtCfg) and
args = count(format_expr.getAnArg())
)
}
/** A string constant that has the `format` method applied to it. */
class AdvancedFormatString extends PossibleAdvancedFormatString {
AdvancedFormatString() { advanced_format_call(_, this, _) }
}
/** A string formatting operation that uses the `format` method. */
class AdvancedFormattingCall extends Call {
AdvancedFormattingCall() { advanced_format_call(this, _, _) }
/** Count of the arguments actually provided */
int providedArgCount() { advanced_format_call(this, _, result) }
/** Gets a formatting string for this call. */
AdvancedFormatString getAFormat() { advanced_format_call(this, result, _) }
}