Repository navigation
Fix medium-severity CodeQL alerts in openig-core - #171
Conversation
maximthomas
left a comment
There was a problem hiding this comment.
praise: The temp-file fix sits exactly where alert #8 points and closes the TOCTOU window along with it.
Script.getGroovyScriptCacheDir()now usesFiles.createTempDirectory, which replaces thecreateTempFile/delete/mkdirrace and the ignoredmkdir()result with one atomic, owner-only call.- Cached inline scripts are created with
Files.createTempFileand written in try-with-resources, so a failedwriteno longer leaks theFileWriter. ScriptTestasserts the actual POSIX permissions and skips withSkipExceptionon non-POSIX file systems, so the Windows legs stay green.
issue (non-blocking): The consumed message body is still logged raw at INFO when no root handler is set.
openig-core/src/main/java/org/openidentityplatform/openig/mq/MQ_IBM.java:201-203
The consumer threads start during heap init, before HttpFrameworkServlet sets its root handler, so messages already queued at startup take the getRootHandler()==null branch. There, request.getEntity().toString() returns the body set at line 178 unchanged, so a body like x\n12:00:00:000 | INFO | ... forges a line at INFO, the shipped root level, while the fixed line 173 only logs at TRACE. The line predates this PR, but it logs the same source that the comment at line 172 says "must not forge log lines". MQ_Kafka.java:178 has the same sink.
logger.info("process done: {}", request.getEntity().toString().replaceAll("[\\r\\n]", " "));Or: forLog(request.getEntity()), if the helper from the next comment is extracted.
suggestion (non-blocking): Neither MQ_IBM change is pinned by a test.
openig-core/src/main/java/org/openidentityplatform/openig/mq/MQ_IBM.java:155, :173
If line 155 goes back to value or line 173 goes back to message, the suite stays green. MQ_IBMTest is the only test that references MQ_IBM, and it neither asserts nor captures log output. The description explains why MQ_IBMTest cannot cover these changes (it needs a live broker). Both changes are pure string logic, though, so they can be pinned without a broker.
// MQ_IBM.java
static String forLog(Object message) {
return String.valueOf(message).replaceAll("[\\r\\n]", " ");
}
static Object settingForLog(String key, Object value) {
return WMQConstants.PASSWORD.equals(key) ? "***" : value;
}
// MQ_IBMLogTest.java (TestNG)
@Test
public void shouldStripLineBreaksFromConsumedMessage() {
assertThat(MQ_IBM.forLog("x\r\n12:00:00:000 | INFO | forged")).doesNotContain("\r", "\n");
}
@Test
public void shouldMaskPasswordInSettingsDump() {
assertThat(MQ_IBM.settingForLog(WMQConstants.PASSWORD, "passw0rd")).isEqualTo("***");
assertThat(MQ_IBM.settingForLog(WMQConstants.USERID, "app")).isEqualTo("app");
}Pin: once lines 155 and 173 call these helpers, reverting either helper's body turns its test red.
suggestion (non-blocking): The new permission tests catch a regression only under a group/other-readable umask.
openig-core/src/test/java/org/forgerock/openig/script/ScriptTest.java:51-67
The old File.createTempFile + mkdir code takes its mode from the process umask. Under 022 it yields rwxr-xr-x/rw-r--r-- and both tests fail against it; under 077 it already yields owner-only modes and both tests pass. The ubuntu and macOS legs of build.yml run with the default 022, so CI does pin the fix. A comment stops the next reader from trusting a green run under a restrictive umask.
// These tests tell the fix from the old File.createTempFile()/mkdir() code only when the JVM
// runs with a group/other-readable umask (the CI default 022); under umask 077 both pass anyway.- Script: create the inline Groovy script cache directory with Files.createTempDirectory() and the cached script files with Files.createTempFile(), so both are accessible by the owner only (java/local-temp-file-or-directory-information-disclosure) - MQ_IBM: strip line breaks from the consumed JMS message before writing it to the trace log (java/log-injection), and mask the XMSC_PASSWORD value in the debug dump of the connection factory settings
Address the review of OpenIdentityPlatform#171: - MQ_IBM, MQ_Kafka: the consumed message body was still written to the INFO log as-is when the root handler is not set yet (messages already queued at startup), so a body with line breaks could forge log lines at the shipped root level. Route it, and the Kafka trace of the consumed record (its String key), through the new MQLog.forLog() - MQ_IBM: move the password masking of the settings dump into settingForLog(); MQLogTest pins both helpers - ScriptTest: note that the permission tests tell owner-only from umask-derived permissions only under a group/other-readable umask - MQ_Kafka: add the missing CDDL license header
71e2ff4 to
5a1bc43
Compare
|
Addressed in 5a1bc43:
One correction to the PR description, which said |
maximthomas
left a comment
There was a problem hiding this comment.
praise: The fix sits on every sink the alerts are about, and nowhere else.
Script.java:185Files.createTempDirectorycreates the cache directory atomically with0700, which removes thecreateTempFile→delete→mkdirwindow and the umask dependence; the scripts get0600fromFiles.createTempFile(:154).- One helper,
MQLog.forLog, covers all four consumed-data sinks (MQ_IBM.java:179,:208,MQ_Kafka.java:164,:196), including the INFOprocess donebranch taken before the root handler is set. MQ_IBM.settingForLogmasksWMQConstants.PASSWORDin the settings dump.
Closes the two open
mediumJava code scanning alerts (#8, #9) and, on the way, note #54.java/local-temp-file-or-directory-information-disclosureScript.javaFile.createTempFile()→delete()→mkdir(): umask permissions (typically0755, readable by other local users) plus a TOCTOU window between delete and mkdir. The cached scripts are thesourceof route configs, which may embed credentials. Now created withFiles.createTempDirectory()(atomic,0700); the script files withFiles.createTempFile()(0600);FileWriterin try-with-resources. Also closes note #54 (java/ignored-error-status-of-callon the removedmkdir()).java/log-injectionMQ_IBM.java,MQ_Kafka.javaINFOlog (the shipped root level) when the root handler is not set yet — the consumers start with the heap, so messages already queued at startup take that branch.MQ_Kafkahas the same two sinks; its trace line prints the consumed record with itsStringkey. All four now go throughMQLog.forLog(), which replaces line breaks.MQ_IBM.javaXMSC_PASSWORD. The password value is now masked (MQ_IBM.settingForLog()).Tests added:
ScriptTest:shouldCreateInlineScriptCacheDirectoryReadableByOwnerOnlyandshouldCreateInlineScriptFileReadableByOwnerOnly— both failed before the fix withGROUP_READ, GROUP_EXECUTE, OTHERS_READ, OTHERS_EXECUTEpresent. They tell the fix from umask-derived permissions only under a group/other-readable umask such as the default022, and are skipped on non-POSIX file systems (the Windows runners in the build matrix).MQLogTest: pinsMQLog.forLog()andMQ_IBM.settingForLog()— with either helper body reverted to returning its input, its test fails. The handlers themselves are not exercised:MQ_IBMTestruns in the suite, but without a live IBM MQ broker (see thedocker runcomment in it) it only goes through the connection-failure paths and asserts nothing.MQ_IBM.javaandMQ_Kafka.javahad no license header; both were created in 2021 by 3A Systems, so a CDDL header withCopyright 2021-2026 3A Systems, LLC.is added.