Skip to content

Commit 315ffdf

Browse files
author
Alvaro Muñoz
committed
Improve env var injection sanitizers
1 parent fef37b6 commit 315ffdf

1 file changed

Lines changed: 30 additions & 19 deletions

File tree

ql/lib/codeql/actions/security/EnvVarInjectionQuery.qll

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ string sanitizerCommand() {
1010
result =
1111
[
1212
"tr\\s+(-d\\s*)?('|\")?.n('|\")?", // tr -d '\n' ' ', tr '\n' ' '
13-
"tr\\s+-cd\\s+.*:alpha:", // tr -cd '[:alpha:_]'
13+
"tr\\s+-cd\\s+.*:al(pha|num):", // tr -cd '[:alpha:_]'
1414
"(head|tail)\\s+-n\\s+1" // head -n 1, tail -n 1
1515
]
1616
}
@@ -55,18 +55,23 @@ class EnvVarInjectionFromFileReadSink extends EnvVarInjectionSink {
5555
* echo "COMMIT_MESSAGE=${COMMIT_MESSAGE}" >> $GITHUB_ENV
5656
*/
5757
class EnvVarInjectionFromCommandSink extends EnvVarInjectionSink {
58+
CommandSource inCommand;
59+
string injectedVar;
60+
string command;
61+
5862
EnvVarInjectionFromCommandSink() {
59-
exists(CommandSource source, Run run, string var |
60-
this.asExpr() = source.getEnclosingRun().getScript() and
61-
run = source.getEnclosingRun() and
62-
run.getScript().getACmdReachingGitHubEnvWrite(source.getCommand(), var) and
63+
exists(Run run |
64+
this.asExpr() = inCommand.getEnclosingRun().getScript() and
65+
run = inCommand.getEnclosingRun() and
66+
run.getScript().getACmdReachingGitHubEnvWrite(inCommand.getCommand(), injectedVar) and
6367
(
64-
not run.getScript().getACmdReachingGitHubEnvWrite(_, var)
68+
// the source flows to the injected variable without any command in between
69+
not run.getScript().getACmdReachingGitHubEnvWrite(_, injectedVar) and
70+
command = ""
6571
or
66-
exists(string sanitizer |
67-
run.getScript().getACmdReachingGitHubEnvWrite(sanitizer, var) and
68-
not exists(sanitizer.regexpFind(sanitizerCommand(), _, _))
69-
)
72+
// the source flows to the injected variable with a command in between
73+
run.getScript().getACmdReachingGitHubEnvWrite(command, injectedVar) and
74+
not command.regexpMatch(".*" + sanitizerCommand() + ".*")
7075
)
7176
)
7277
}
@@ -81,18 +86,24 @@ class EnvVarInjectionFromCommandSink extends EnvVarInjectionSink {
8186
* echo "FOO=$BODY" >> $GITHUB_ENV
8287
*/
8388
class EnvVarInjectionFromEnvVarSink extends EnvVarInjectionSink {
89+
string inVar;
90+
string injectedVar;
91+
string command;
92+
8493
EnvVarInjectionFromEnvVarSink() {
85-
exists(Run run, string var_name, string var |
86-
exists(run.getInScopeEnvVarExpr(var_name)) and
94+
exists(Run run |
8795
run.getScript() = this.asExpr() and
88-
run.getScript().getAnEnvReachingGitHubEnvWrite(var_name, var) and
96+
exists(run.getInScopeEnvVarExpr(inVar)) and
97+
run.getScript().getAnEnvReachingGitHubEnvWrite(inVar, injectedVar) and
8998
(
90-
not run.getScript().getACmdReachingGitHubEnvWrite(_, var)
99+
// the source flows to the injected variable without any command in between
100+
not run.getScript().getACmdReachingGitHubEnvWrite(_, injectedVar) and
101+
command = ""
91102
or
92-
exists(string sanitizer |
93-
run.getScript().getACmdReachingGitHubEnvWrite(sanitizer, var) and
94-
not exists(sanitizer.regexpFind(sanitizerCommand(), _, _))
95-
)
103+
// the source flows to the injected variable with a command in between
104+
run.getScript().getACmdReachingGitHubEnvWrite(_, injectedVar) and
105+
run.getScript().getACmdReachingGitHubEnvWrite(command, injectedVar) and
106+
not command.regexpMatch(".*" + sanitizerCommand() + ".*")
96107
)
97108
)
98109
}
@@ -122,7 +133,7 @@ class EnvVarInjectionFromMaDSink extends EnvVarInjectionSink {
122133
private module EnvVarInjectionConfig implements DataFlow::ConfigSig {
123134
predicate isSource(DataFlow::Node source) {
124135
source instanceof RemoteFlowSource and
125-
not source.(RemoteFlowSource).getSourceType() = "branch"
136+
not source.(RemoteFlowSource).getSourceType() = ["branch", "username"]
126137
}
127138

128139
predicate isSink(DataFlow::Node sink) { sink instanceof EnvVarInjectionSink }

0 commit comments

Comments
 (0)