Skip to content

Commit 84db72a

Browse files
owen-mcCopilot
andcommitted
Centralize command argument safety checks
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent c2e8ec7 commit 84db72a

1 file changed

Lines changed: 63 additions & 65 deletions

File tree

‎java/ql/lib/semmle/code/java/security/CommandArguments.qll‎

Lines changed: 63 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -13,66 +13,10 @@ import semmle.code.java.Collections
1313
* i.e. it's not in a position that selects a command to execute.
1414
*/
1515
predicate isSafeCommandArgument(Expr ex) {
16-
exists(ArrayInit ai, int i |
17-
ex = ai.getInit(i) and
16+
exists(CommandArguments arguments, int i |
1817
i > 0 and
19-
not isShell(ai.getInit(0)) and
20-
(
21-
not exists(delegatedCommandIndex(ai.getInit(0)))
22-
or
23-
exists(int commandIndex |
24-
commandIndex = delegatedCommandIndex(ai.getInit(0)) and
25-
(
26-
i < commandIndex
27-
or
28-
i > commandIndex and
29-
not isShell(ai.getInit(commandIndex)) and
30-
not exists(delegatedCommandIndex(ai.getInit(commandIndex)))
31-
)
32-
)
33-
)
34-
)
35-
or
36-
exists(CommandArgumentList cal, int i |
37-
not cal.isShell() and
38-
i > 0 and
39-
ex = cal.getArgument(i) and
40-
(
41-
not exists(delegatedCommandIndex(cal.getArgument(0)))
42-
or
43-
exists(int commandIndex |
44-
commandIndex = delegatedCommandIndex(cal.getArgument(0)) and
45-
(
46-
i < commandIndex
47-
or
48-
i > commandIndex and
49-
exists(cal.getArgument(commandIndex)) and
50-
not isShell(cal.getArgument(commandIndex)) and
51-
not exists(delegatedCommandIndex(cal.getArgument(commandIndex)))
52-
)
53-
)
54-
)
55-
)
56-
or
57-
exists(CommandArgArrayImmutableFirst caa, int i |
58-
not caa.isShell() and
59-
i > 0 and
60-
ex = caa.getAWrite(i) and
61-
(
62-
not exists(delegatedCommandIndex(caa.getFirstElement()))
63-
or
64-
exists(int commandIndex |
65-
commandIndex = delegatedCommandIndex(caa.getFirstElement()) and
66-
(
67-
i < commandIndex
68-
or
69-
i > commandIndex and
70-
exists(caa.getAnElement(commandIndex)) and
71-
not isShell(caa.getAnElement(commandIndex)) and
72-
not exists(delegatedCommandIndex(caa.getAnElement(commandIndex)))
73-
)
74-
)
75-
)
18+
ex = arguments.getArgumentUse(i) and
19+
arguments.hasSafeArgument(i)
7620
)
7721
}
7822

@@ -116,6 +60,66 @@ private int delegatedCommandIndex(Expr ex) {
11660
)
11761
}
11862

63+
private newtype TCommandArguments =
64+
TLiteralArguments(ArrayInit init) or
65+
TListArguments(CommandArgumentList list) or
66+
TMutableArguments(CommandArgArrayImmutableFirst array)
67+
68+
/** An ordered sequence of command arguments. */
69+
private class CommandArguments extends TCommandArguments {
70+
string toString() { result = this.getArgument(0).toString() }
71+
72+
/** Gets a possible value at the given index. */
73+
private Expr getArgument(int index) {
74+
exists(ArrayInit init | this = TLiteralArguments(init) | result = init.getInit(index))
75+
or
76+
exists(CommandArgumentList list |
77+
this = TListArguments(list) and
78+
result = list.getArgument(index)
79+
)
80+
or
81+
exists(CommandArgArrayImmutableFirst array |
82+
this = TMutableArguments(array) and
83+
result = array.getAnElement(index)
84+
)
85+
}
86+
87+
/** Gets an expression whose value is stored at the given index. */
88+
Expr getArgumentUse(int index) {
89+
exists(ArrayInit init | this = TLiteralArguments(init) | result = init.getInit(index))
90+
or
91+
exists(CommandArgumentList list |
92+
this = TListArguments(list) and
93+
result = list.getArgument(index)
94+
)
95+
or
96+
exists(CommandArgArrayImmutableFirst array |
97+
this = TMutableArguments(array) and
98+
result = array.getAWrite(index)
99+
)
100+
}
101+
102+
predicate hasSafeArgument(int index) {
103+
exists(this.getArgumentUse(index)) and
104+
not isShell(this.getArgument(0)) and
105+
(
106+
not exists(delegatedCommandIndex(this.getArgument(0)))
107+
or
108+
exists(int commandIndex |
109+
commandIndex = delegatedCommandIndex(this.getArgument(0)) and
110+
(
111+
index < commandIndex
112+
or
113+
index > commandIndex and
114+
exists(this.getArgument(commandIndex)) and
115+
not isShell(this.getArgument(commandIndex)) and
116+
not exists(delegatedCommandIndex(this.getArgument(commandIndex)))
117+
)
118+
)
119+
)
120+
}
121+
}
122+
119123
/**
120124
* A type that could be a list of strings. Includes raw `List` types.
121125
*/
@@ -184,9 +188,6 @@ private class CommandArgumentList extends SsaExplicitWrite {
184188

185189
/** Gets the argument added at the given index. */
186190
Expr getArgument(int index) { result = this.getAnAddAtIndex(index).getArgument(0) }
187-
188-
/** Holds if the first element of this list is a shell command. */
189-
predicate isShell() { isShell(this.getArgument(0)) }
190191
}
191192

192193
/**
@@ -252,9 +253,6 @@ private class CommandArgArrayImmutableFirst extends CommandArgumentArray {
252253
result = arrayElementOf(this.getDefiningExpr(), index)
253254
)
254255
}
255-
256-
/** Holds if the first element of this array is a shell command. */
257-
predicate isShell() { isShell(this.getFirstElement()) }
258256
}
259257

260258
/** Gets an element of an immutable array of strings. */

0 commit comments

Comments
 (0)