-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Java: Add Guard Classes for checking OS & unify System Property Access #8032
Conversation
a31231f
to
e5c7e88
Compare
} | ||
} else if (!Files.exists(tempDirChild)) { | ||
// On Windows, we still need to create the directory, when it doesn't already exist. | ||
Files.createDirectory(tempDirChild); // GOOD: Windows doesn't share the temp directory between users |
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.
Without the OS guard changes, this gets flagged as vulnerable
} | ||
} else if (!Files.exists(tempDirChild)) { | ||
// On Windows, we still need to create the directory, when it doesn't already exist. | ||
Files.createDirectories(tempDirChild); // GOOD: Windows doesn't share the temp directory between users |
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.
Without the OS guard changes, this also gets flagged as vulnerable
@@ -30,7 +34,7 @@ void exampleSafeWithWindowsSupportFile() { | |||
createTempFile(tempChildFile.toPath()); // GOOD: Good has permissions `-rw-------` | |||
} | |||
|
|||
static void createTempFile(Path tempDir) { | |||
static void createTempFile(Path tempDirChild) { |
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.
Simple fix because this wouldn't have compiled earlier
0aabaa5
to
f55ef0e
Compare
Thanks @intrigus-lgtm for the feedback! 🙂 |
d50e09b
to
2896c0c
Compare
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.
Would it make sense to also consider OperatingSystemMXBean.getName()
?
Sometimes the OS path separator (File.pathSeparator
, File.pathSeparatorChar
) and OS file path separator (File.separator
, File.separatorChar
) are used for OS detection; examples:
- https://github.com/openjdk/jdk/blob/d7f31d0d53bfec627edc83ceb75fc6202891e186/src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java#L673-L674
- https://github.com/openjdk/jdk/blob/d7f31d0d53bfec627edc83ceb75fc6202891e186/src/jdk.compiler/share/classes/com/sun/tools/sjavac/JavacState.java#L913
Would it make sense to consider these as well? (That is, comparison in EqualityTest
, and maybe EqualsMethod
call (qualifier or argument), where the compared value is a compile time constant)
(Feel free to consider this only as suggestion; I am not a member of this project)
java/ql/src/Security/CWE/CWE-200/TempDirLocalInformationDisclosure.ql
Outdated
Show resolved
Hide resolved
97ca09a
to
cf2bfb5
Compare
Co-authored-by: Marcono1234 <Marcono1234@users.noreply.github.com>
8a8f0fc
to
82d3cd8
Compare
Co-authored-by: Chris Smowton <smowton@github.com>
This has some QLDoc errors:
Also, a bunch of autoformatting required:
|
Having to revert #8360 because of apparent performance problems. Remind me why you needed concatenation of string + char again? If you still want it, a few possibilities to investigate:
|
For this particular change, I don't really, I wrote that new predicate to be more extensive than I needed so that it was generally more useful. In this particular PR, I just need to do equality checking between both string and characters because the new system property predicate sometimes returns a expression that has the type Would adding the |
No, |
I have no idea what this would look like unfortunately. I'd need some pointers on what the code would look like here. |
Probably not worth the difficulty just for this. I've made #8407 to revert the original addition of CharacterLiteral to getStringValue per Marcono's objections in #8325, and recommend you simply special-case CharacterLiteral in this PR as you did originally for the time being. We can investigate how to do this in a performant manner as and when the need arises. |
b73c74a
to
50ff2c2
Compare
I'm ready for a final PR review or a merge if everyone is happy with this in it's current state. 😃 |
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 believe we don't have tests for the Guava cases yet?
I also made a minor commit above to improve some class names. |
Done |
Anything else required here? |
Just running a new performance evaluation as I realised the first one only considered queries with precision >= high, whereas the temp-dir-local-info query has precision = medium |
Yeay! Very happy this is finally merged! |
private class IsNotUnixBarrierGuard extends WindowsOsBarrierGuard instanceof IsUnixGuard { | ||
override predicate checks(Expr e, boolean branch) { | ||
this.controls(e.getBasicBlock(), branch.booleanNot()) | ||
} | ||
} | ||
|
||
private class IsWindowsBarrierGuard extends WindowsOsBarrierGuard instanceof IsWindowsGuard { | ||
override predicate checks(Expr e, boolean branch) { this.controls(e.getBasicBlock(), branch) } | ||
} | ||
|
||
private class IsSpecificWindowsBarrierGuard extends WindowsOsBarrierGuard instanceof IsSpecificWindowsVariant { | ||
override predicate checks(Expr e, boolean branch) { | ||
branch = true and this.controls(e.getBasicBlock(), branch) | ||
} | ||
} |
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.
These three barrier-guards don't really make sense. Using Guard.controls
in the checks
predicate of a barrier-guard is almost guaranteed to be completely wrong, as the barrier-guard abstraction is explicitly designed to handle the .controls
-aspect of guards.
The expression e
should be the variable access being checked by the guard, and the branch
should be the result of the guard that indicates that e
is safe. So the basic implementation hint for the checks
predicate should just be to use the AST and nothing more. If you really want to use Guard.controls
to pick out the nodes that are sanitised then you're really just defining a regular sanitizer and not a sanitizer-guard.
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.
So, I partially understand what you're saying. That being said, I don't know how to fix it.
Also, could you suggest a test case that this doesn't work for that I could use as a unit test
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.
A barrierguard is just syntactic sugar for a sanitizer with a specific pattern. To see how a barrier-guard gets desugared into a sanitizer, look at the getAGuardedNode()
predicate:
final Node getAGuardedNode() {
exists(SsaVariable v, boolean branch, RValue use |
this.checks(v.getAUse(), branch) and
use = v.getAUse() and
this.controls(use.getBasicBlock(), branch) and
result.asExpr() = use
)
}
You end up just joining this.controls
with itself, which semantically is mostly harmless. But you can simplify things by just defining the sanitizers directly. E.g. replace IsSpecificWindowsBarrierGuard
with the following:
abstract private class WindowsOsSanitizer extends DataFlow::Node { }
private class IsSpecificWindowsSanitizer extends WindowsOsSanitizer {
IsSpecificWindowsSanitizer() {
any(IsSpecificWindowsVariant guard).control(this.asExpr().getBasicBlock(), true)
}
}
...
override predicate isSanitizer(DataFlow::Node node) {
node instanceof WindowsOsSanitizer
}
This is mostly equivalent to your IsSpecificWindowsBarrierGuard
except it drops the restriction that only uses of ssa variables are sanitized.
The other two, IsNotUnixBarrierGuard
and IsWindowsBarrierGuard
, can be similarly rewritten, but note that they are somewhat broken as-is, since they currently guard both of their branches. In
if (<IsWindowsBarrierGuard>) {
foo
} else {
bar
}
you're sanitizing both foo
and bar
.
private predicate isOsFromApacheCommons(FieldAccess fa, string fieldNamePattern) { | ||
exists(Field f | f = fa.getField() | | ||
f.getDeclaringType() instanceof TypeApacheSystemUtils and | ||
f.getName().matches(fieldNamePattern) |
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 guess it is unlikely that this will give spurious matches, but do note that _
is a wildcard for the .matches
predicate, so all of the underscores in the names below could be matched with anything.
This is useful when you have a bit of logic that is only vulnerable on specific OSes. Additionally, it unifies the access of all system properties (from all libraries and JDK APIs) into one method call
getSystemProperty(_)
.