-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Py: add unsafe-shell-command-construction #12047
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
QHelp previews: python/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.qhelpUnsafe shell command constructed from library inputDynamically constructing a shell command with inputs from library functions may inadvertently change the meaning of the shell command. Clients using the exported function may use inputs containing characters that the shell interprets in a special way, for instance quotes and spaces. This can result in the shell command misbehaving, or even allowing a malicious user to execute arbitrary commands on the system. RecommendationIf possible, provide the dynamic arguments to the shell as an array to APIs such as Alternatively, if the shell command must be constructed dynamically, then add code to ensure that special characters do not alter the shell command unexpectedly. ExampleThe following example shows a dynamically constructed shell command that downloads a file from a remote URL. import os
def download(path):
os.system("wget " + path) # NOT OK The shell command will, however, fail to work as intended if the input contains spaces or other special characters interpreted in a special way by the shell. Even worse, a client might pass in user-controlled data, not knowing that the input is interpreted as a shell command. This could allow a malicious user to provide the input To avoid such potentially catastrophic behaviors, provide the input from library functions as an argument that does not get interpreted by a shell: import subprocess
def download(path):
subprocess.run(["wget", path]) # OK References
|
python/ql/lib/semmle/python/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll
Fixed
Show fixed
Hide fixed
c685cfc
to
6c2f5ed
Compare
It's a very direct port from Ruby, with only minor adjustments to fit the Python APIs
…s flipped compared to JS. Thanks Copilot!
/** | ||
* Gets an AST node that is exported by a library. | ||
*/ | ||
private AstNode getAnExportedLibraryFeature() { | ||
result.(Module).getFile() = getALibraryExportedContainer() | ||
or | ||
result = getAnExportedLibraryFeature().(Module).getAStmt() | ||
or | ||
result = getAnExportedLibraryFeature().(ClassDef).getDefinedClass().getAMethod() | ||
or | ||
result = getAnExportedLibraryFeature().(ClassDef).getDefinedClass().getInitMethod() | ||
or | ||
result = getAnExportedLibraryFeature().(FunctionDef).getDefinedFunction() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The equivalent predicate in JavaScript is way bigger, with more cases in the disjunction.
I imagine this will grow over time, but for now I'll start with what I've seen a need for.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good, thanks 👍 I would like to see a few minor changes, and then we need to discuss the precision of the query a bit 👍
python/ql/src/Security/CWE-078/examples/unsafe-shell-command-construction.py
Outdated
Show resolved
Hide resolved
python/ql/src/Security/CWE-078/examples/unsafe-shell-command-construction_fixed.py
Outdated
Show resolved
Hide resolved
python/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.qhelp
Outdated
Show resolved
Hide resolved
* @kind path-problem | ||
* @problem.severity error | ||
* @security-severity 6.3 | ||
* @precision high |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's talk a bit more about what the precision should be.
Many of the results are just 🤦 without being security relevant, but they are bad enough style that I think the results are OK regardless of whether it's a security issue.
This ☝️ part of your PR comment makes it sound like most of the results are following a bad pattern that ideally should be fixed. But also that it's like it is a code smell and not a security issue.
Based on that, I'm thinking it might be more appropriate to use @precision medium
, but happy to hear your thoughts on this.
(I looked over some of the MRVA results, but most of the ones I looked at seemed "meh" to me)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's a code-smell with high precision, but a security issue with medium precision.
So I suggest that we lower the severity to warning
instead of lowering the precision.
Thoughts?
The query is almost direct copy-paste from Ruby.
The query doesn't (currently) flag any CVEs, but that will hopefully happen in the future.
Evaluation looks OK.
Shows a slight slowdown from adding a new query, but I can't find a reason for the slowdown when I try locally.
Full MRVA run.
Many of the results are just 🤦 without being security relevant, but they are bad enough style that I think the results are OK regardless of whether it's a security issue.
The results in
localstack
are FPs, due to it spuriously finding that shell can be set to True. But I don't think we can fix that.Lots of the results are file paths inserted into shell strings.