Repository navigation
Fix mismatched placeholders in log and exception messages - #173
Conversation
maximthomas
left a comment
There was a problem hiding this comment.
praise: Each fix puts the placeholder where the argument already was, and the two testable fixes get tests that fail at the base.
Heaplets.java:86now logsc.getName()through{}instead of a literal%s.PolicyEnforcementFilter.java:325has two{}for its two arguments and balanced parentheses.HeapletsTest.shouldLogClassNameWhenHeapletCannotBeInstantiatedcaptures the WARN with a logbackListAppenderand asserts the exact text.
suggestion (non-blocking): The %s argument in ResolveLocationJsonValueFunction.fetch is always null, so the message never shows a $location value.
openig-core/src/main/java/org/forgerock/openig/util/ResolveLocationJsonValueFunction.java:104-108, :72
fetch() receives the node after as(evaluated(bindings)) (:72). as(url()) returns null only when the value isNull() (commons util JsonValueFunctions.TO_URL); a malformed or non-string value throws before this point. So {"$location": null} and {"$location": "${missing}"} both produce $location value (null) cannot be null (or evaluated to null). The expression that evaluated to null is lost, although the commit body says the exception now "reports the $location value". Passing the raw value through keeps the alert fixed and makes the message useful:
if (node.isMap() && node.size() == 1 && node.isDefined("$location")) {
JsonValue location = node.get("$location");
return fetch(location.as(evaluated(bindings)), location.getObject());
}
private JsonValue fetch(JsonValue location, Object expression) {
URL url = location.as(url());
if (url == null) {
throw new JsonValueException(location,
format("$location value (%s) cannot be null (or evaluated to null)",
expression));
}Pin: a case with "$location": "${missing}" that asserts $location value (${missing}). It fails against the current code.
note (non-blocking): The commit text does not match the behaviour.
openig-core/src/main/java/org/forgerock/openig/util/ResolveLocationJsonValueFunction.java: the commit says the exception now reports the$locationvalue, but on this branch the value is alwaysnull(see the suggestion above).
- Heaplets: use the SLF4J "{}" placeholder instead of "%s", so the
warning names the class that could not be instantiated
- ResolveLocationJsonValueFunction: use "%s" in String.format() instead
of "{}", so the exception reports the $location value
- PolicyEnforcementFilter: add the missing placeholder for the request
URI in the "returned resource does not match" error
The $location node was evaluated before fetch(), and url() returns null
only for a null node, so the exception always read
"$location value (null)". Evaluate the node inside fetch() and print
the raw value, so "${missing}" is reported as
"$location value (${missing})".
f0f68c1 to
f2ea5a1
Compare
|
Both points are addressed in f2ea5a1; the branch is also rebased onto current
|
maximthomas
left a comment
There was a problem hiding this comment.
praise: Moving the $location evaluation into fetch() makes the #30 message report what the user configured, without changing anything else the exception shows.
fetch()evaluates the value itself (openig-core/src/main/java/org/forgerock/openig/util/ResolveLocationJsonValueFunction.java:104) and formats the rawlocation.getObject()(:106-108), so"${missing}"is reported as$location value (${missing});shouldReportTheExpressionWhenLocationEvaluatesToNullpins it.- The exception's pointer is unchanged:
ExpressionJsonTransformFunction.applyreturns the evaluated value with the input's pointer (openig-core/src/main/java/org/forgerock/openig/util/ExpressionJsonTransformFunction.java:73), andJsonValueException.getMessage()renders only that pointer, so the IOException message atResolveLocationJsonValueFunction.java:115reads as before.
Closes the 16 `note`-level code scanning alerts that are worth fixing (#10–#15, #32, #46–#53, #58). The remaining notes are either pure `@Override`/`static` cosmetics (done separately in #180) or by-design findings to be dismissed. ### `openig-oauth2` — `OAuth2Session` | Alert | Change | |---|---| | #32 `missing-space-in-concatenation` | `"…ID token" + "could not be decoded"` produced `ID tokencould not be decoded` in the `server_error` description. | | #53 `uncaught-number-format-exception` | A string `expires_in` that is not a number now raises `OAuth2ErrorException(E_SERVER_ERROR, "'expires_in' field value is not a number: …")`, consistent with the sibling "neither a Number nor a String" branch, instead of a raw `NumberFormatException`. | | #58 `local-shadows-field` | The merged token response local is renamed `mergedResponse`; its uses (`extractIdToken`, the constructor) are pointed at it. No behavior change — `testStateRefreshed` covers the merge semantics. | ### `openig-core` | Alert | Change | |---|---| | #48, #49 `uncaught-number-format-exception` | `ICAPFilter`: a `connect_timeout` / `read_timeout` that is not an `int` now fails with a `JsonValueException` that points at the field (`/connect_timeout: Expecting a number of milliseconds that fits in an int, got: soon`) instead of `NumberFormatException: For input string: "soon"`. | | #12 `deprecated-call` (`Object.finalize`) | `LdapClient.finalize()` removed: the class is a singleton (`INSTANCE`), so the finalizer never ran. | | #11, #13 `deprecated-call` (`Class.newInstance`) | `Heaplets`, `JsonValues`: `getDeclaredConstructor().newInstance()`. The `InvocationTargetException` it adds is unwrapped so that an `Error` thrown by the constructor still propagates as it did with `Class.newInstance()`. Otherwise `JsonValues.asNewInstance` makes the constructor's own exception the cause of a `JsonValueException` (other reflective failures are caught as `ReflectiveOperationException`), and `Heaplets.getHeaplet` logs and ignores it as before. **One difference:** an `ExceptionInInitializerError` raised inside the constructor (by touching a class whose static initializer fails) used to be wrapped by `asNewInstance`'s `catch (ExceptionInInitializerError …)`; it now propagates like any other `Error`, as it already did from `Class.forName(name, true, …)` for the target class itself and from `Heaplets.getHeaplet`. | | #10 `deprecated-call` | `OpenApiMockResponseHandler`: `responses.get(ApiResponses.DEFAULT)`. | | #50 `uncaught-number-format-exception` | `Principal`: the `org.openidentityplatform.openig.websocket.ttl` property is parsed once at class load by `ttlMillis(String)`; an invalid value logs a warning and falls back to 180 s instead of throwing on every websocket message. `authorize()` checks the interval through `expired(now)`. **Semantic change:** the property used to be re-read on every `authorize()` call, i.e. it could be changed at runtime with `System.setProperty`. I assumed that is not a supported use — say so if it is. | | #46, #47 `uncaught-number-format-exception` | `MockDataGenerator`: latitude/longitude generated with `FAKER.number().randomDouble(6, …)` instead of parsing a locale-formatted string. | ### `openig-doc` — `SampleApplication` | Alert | Change | |---|---| | #14, #15 `deprecated-call` | `validateConfiguration(true)` + `createSSLContext()` → `createSSLContext(true)`, catching `SSLContextConfigurator.GenericStoreException`, which grizzly-framework 3.0.1 (the version `openig-doc` resolves, through `opendj-grizzly`) throws when the bundled key store cannot be loaded. The configurator is created with `SSLContextConfigurator(false)`, so it no longer reads the `javax.net.ssl.*` system properties: the sample sets its own key store and passwords, and `createSSLContext(true)` only logs a missing key store provider, which would have left the HTTPS listener without a key. Exercised by the `openig-doc` integration tests, which start the sample with HTTPS. | | #51, #52 `uncaught-number-format-exception` | A non-numeric port argument prints `Port numbers must be integers: …` plus the usage and exits, instead of a stack trace. | ### Tests - `OAuth2SessionTest`: `shouldStateAuthorizedWithNonNumericExpiresInFailWithOAuth2Error` (failed before with `NumberFormatException: For input string: "soon"`; asserts the `server_error` code) and `shouldStateAuthorizedWithUndecodableIdTokenFailWithReadableMessage` (failed before with `ID tokencould`). - `ICAPFilterTest` (new): configured and default (5000 / 15000 ms) timeouts are applied; a non-numeric `connect_timeout` / `read_timeout` (data provider) fails with a `JsonValueException` naming the field — failed before with a bare `NumberFormatException`. - `PrincipalTest` (new): `ttlMillis` — seconds to millis, trimming, and the fallback for `null` / empty / non-numeric values; `shouldReauthorizeWithTtlReadAtClassLoad` and `shouldAuthorizeFromCacheWithTtlReadAtClassLoad` pin that the interval is read once at class load, in `expired()` and through `authorize()`; `shouldExpireWithoutCachedResult` pins that a missing result is always refreshed. - `JsonValuesTest`, `HeapletsTest`: an `Error` thrown by the constructor propagates, including an `ExceptionInInitializerError` raised in it (`asNewInstance`); an exception is wrapped in a `JsonValueException` (`asNewInstance`) or ignored (`getHeaplet`). - `MockDataGeneratorTest.generate_returnsCoordinatesInRange`, `OpenApiMockResponseHandlerTest.bestResponseSchema_fallsBackToDefaultResponse`. Each test added in the review rounds was run against the mutant it targets (per-call TTL read in `expired()` and in `authorize()`, `res==null` dropped from `expired()`, `Error` wrapped or swallowed, `ExceptionInInitializerError` wrapped again, `E_INVALID_REQUEST`, swapped ICAP defaults, latitude in ±180, `return null` for `default`): all ten fail. Verified, rebased on current `master`: `openig-core` 1029 tests, `openig-oauth2` 160 and `openig-doc` 11 (integration tests, `testGetLoginPageHttps` included) under `mvn verify`, javadoc with `failOnWarnings` included. #173, which also touches `Heaplets.java` (the `warn` line, left untouched here) and `HeapletsTest.java`, has been merged; the branch is rebased onto it, and `HeapletsTest` keeps the tests of both PRs.
Closes code scanning alerts #29, #30, #31 (
java/unused-format-argument, warning). All three are real message bugs: the argument was supplied but never rendered.Heaplets.javalogger.warn("… instantiate %s as a Heaplet", c.getName(), e)— SLF4J logs a literal%s, the class name is lost{}ResolveLocationJsonValueFunction.javaString.format("$location value ({}) cannot be null …", …)— exception message shows a literal{}%s, filled with the raw (unevaluated)$locationvalue, so"${missing}"is reported as$location value (${missing})instead of(null)PolicyEnforcementFilter.java"Returned resource ('{}' does not match current request URI (''))"— second placeholder missing,originalnever printed, unbalanced parentheses('{}') … ('{}')Tests added:
HeapletsTest.shouldLogClassNameWhenHeapletCannotBeInstantiated— captures the WARN with a logbackListAppendervia a heaplet whose constructor throws; failed before the fix with…instantiate [%s] as a Heaplet.ResolveLocationJsonValueFunctionTest.shouldReportTheValueWhenLocationIsNull— failed before the fix with$location value ({}) cannot be null ….ResolveLocationJsonValueFunctionTest.shouldReportTheExpressionWhenLocationEvaluatesToNull—"$location": "${missing}"; failed with$location value (null) …while the value was evaluated beforefetch().No test for #31:
openig-openamruns its tests onslf4j-nop, so the log line cannot be captured without adding a logging backend to the module's test dependencies; the change is confined to the format string of a// Should never happenbranch.