Conversation
| } | ||
| if (keystoreFile.exists()) { | ||
| try { | ||
| keystore.load(new FileInputStream(keystoreFile), keystorePassword.toCharArray()); |
Check warning
Code scanning / CodeQL
Potential input resource leak Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 21 hours ago
In general, the fix is to ensure that every FileInputStream and FileOutputStream created in this method is closed deterministically, even if an exception occurs. In Java 7+, the idiomatic way is to use try-with-resources, which automatically closes AutoCloseable resources at the end of the block.
Concretely for this file:
- For loading an existing keystore (line 97), first create a
FileInputStreamvariable in a try-with-resources block and pass that variable tokeystore.load. This guarantees the stream is closed whetherloadsucceeds or fails:try (FileInputStream fis = new FileInputStream(keystoreFile)) { keystore.load(fis, keystorePassword.toCharArray()); }
- For creating a new keystore file (line 108), wrap the
FileOutputStreamin a try-with-resources and pass the variable tokeystore.store:try (FileOutputStream fos = new FileOutputStream(keystoreFile)) { keystore.store(fos, keystorePassword.toCharArray()); }
- For storing the updated keystore at the end (line 122), also use a try-with-resources
FileOutputStream:try (FileOutputStream fos = new FileOutputStream(keystoreFilename)) { keystore.store(fos, keystorePassword.toCharArray()); }
These changes do not alter behavior except to ensure files are properly closed. No new imports are needed because FileInputStream and FileOutputStream are already imported. All modifications occur within KmcKeystoreCreate.main.
| @@ -93,11 +93,11 @@ | ||
| System.exit(3); | ||
| } | ||
| if (keystoreFile.exists()) { | ||
| try { | ||
| keystore.load(new FileInputStream(keystoreFile), keystorePassword.toCharArray()); | ||
| try (FileInputStream fis = new FileInputStream(keystoreFile)) { | ||
| keystore.load(fis, keystorePassword.toCharArray()); | ||
| } catch (Exception e) { | ||
| System.err.println("Failed to load keystore " + keystoreFilename + ", exception: " + e); | ||
| if (e.getMessage().contains("Integrity check failed")) { | ||
| if (e.getMessage() != null && e.getMessage().contains("Integrity check failed")) { | ||
| System.err.println("Failed integrity check could be caused by incorrect keystore password"); | ||
| } | ||
| System.exit(1); | ||
| @@ -105,7 +103,9 @@ | ||
| } else { | ||
| try { | ||
| keystore.load(null, null); // a new keystore | ||
| keystore.store(new FileOutputStream(keystoreFile), keystorePassword.toCharArray()); | ||
| try (FileOutputStream fos = new FileOutputStream(keystoreFile)) { | ||
| keystore.store(fos, keystorePassword.toCharArray()); | ||
| } | ||
| } catch (Exception e) { | ||
| System.err.println("Failed to create keystore " + keystoreFilename + ", exception: " + e); | ||
| System.exit(1); | ||
| @@ -119,7 +119,9 @@ | ||
| KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(secretKey); | ||
| keystore.setEntry(keyName, entry, pp); | ||
|
|
||
| keystore.store(new FileOutputStream(keystoreFilename), keystorePassword.toCharArray()); | ||
| try (FileOutputStream fos = new FileOutputStream(keystoreFilename)) { | ||
| keystore.store(fos, keystorePassword.toCharArray()); | ||
| } | ||
| System.out.println("Added key " + keyName + ", " + keyAlgorithm + ", " + keyMaterialHex); | ||
| } catch (KeyStoreException e) { | ||
| System.err.println("Failed to store key entry to keystore: " + e); |
| } else { | ||
| try { | ||
| keystore.load(null, null); // a new keystore | ||
| keystore.store(new FileOutputStream(keystoreFile), keystorePassword.toCharArray()); |
Check warning
Code scanning / CodeQL
Potential output resource leak Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 21 hours ago
In general, the correct fix is to ensure that every FileOutputStream created is closed in all execution paths, including when exceptions are thrown. In Java 7+, the idiomatic way is to wrap the creation and use of the stream in a try-with-resources block, which guarantees close() is called.
For this file, we should:
- Replace
keystore.store(new FileOutputStream(keystoreFile), ...)in theelsebranch (around line 108) with a try-with-resources that creates aFileOutputStreamvariable, passes it tokeystore.store, and lets the try-with-resources close it. - Replace
keystore.store(new FileOutputStream(keystoreFilename), ...)in the finaltryblock (around line 122) similarly. - Keep the existing exception handling and behavior (
System.exitand printed messages) unchanged; only add the try-with-resources around theFileOutputStreamusage. - No new imports are required because try-with-resources works with
java.io.FileOutputStream, which is already imported.
Concretely:
- In the block creating a new keystore (lines 106–112), wrap the
keystore.store(...)call intry (FileOutputStream fos = new FileOutputStream(keystoreFile)) { ... }, and keep the existingcatchas-is. - In the block that stores the updated keystore after adding the key (lines 115–130), do the same around the
keystore.store(...)call, keeping the existingcatchclauses.
| @@ -105,7 +105,9 @@ | ||
| } else { | ||
| try { | ||
| keystore.load(null, null); // a new keystore | ||
| keystore.store(new FileOutputStream(keystoreFile), keystorePassword.toCharArray()); | ||
| try (FileOutputStream fos = new FileOutputStream(keystoreFile)) { | ||
| keystore.store(fos, keystorePassword.toCharArray()); | ||
| } | ||
| } catch (Exception e) { | ||
| System.err.println("Failed to create keystore " + keystoreFilename + ", exception: " + e); | ||
| System.exit(1); | ||
| @@ -119,7 +121,9 @@ | ||
| KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(secretKey); | ||
| keystore.setEntry(keyName, entry, pp); | ||
|
|
||
| keystore.store(new FileOutputStream(keystoreFilename), keystorePassword.toCharArray()); | ||
| try (FileOutputStream fos = new FileOutputStream(keystoreFilename)) { | ||
| keystore.store(fos, keystorePassword.toCharArray()); | ||
| } | ||
| System.out.println("Added key " + keyName + ", " + keyAlgorithm + ", " + keyMaterialHex); | ||
| } catch (KeyStoreException e) { | ||
| System.err.println("Failed to store key entry to keystore: " + e); |
| KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(secretKey); | ||
| keystore.setEntry(keyName, entry, pp); | ||
|
|
||
| keystore.store(new FileOutputStream(keystoreFilename), keystorePassword.toCharArray()); |
Check warning
Code scanning / CodeQL
Potential output resource leak Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 21 hours ago
In general, the fix is to ensure that every OutputStream or Writer you create is reliably closed, even if an exception occurs. In Java 7+, the preferred approach is a try‑with‑resources statement, which automatically closes any AutoCloseable declared in the try header.
Specifically for KmcKeystoreCreate.main, we need to wrap the FileOutputStream used on line 122 in a try‑with‑resources block so that it is always closed after keystore.store(...) completes, whether normally or exceptionally. We should not otherwise alter the behavior: the same exceptions are still caught by the existing catch blocks. To do this without changing logic, we will:
- Replace line 122 with a try‑with‑resources block that:
- Declares a local
FileOutputStreamvariablefos = new FileOutputStream(keystoreFilename); - Calls
keystore.store(fos, keystorePassword.toCharArray());inside the block.
- Declares a local
- Keep the surrounding
try { ... } catchstructure intact. - No new imports are required, since
FileOutputStreamis already imported.
The similar use of new FileOutputStream(keystoreFile) earlier (line 108) is also technically unclosed, but CodeQL has highlighted only line 122 for this query. Following your instruction to fix the reported error specifically, the change will be limited to the region around line 122.
| @@ -119,7 +119,9 @@ | ||
| KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(secretKey); | ||
| keystore.setEntry(keyName, entry, pp); | ||
|
|
||
| keystore.store(new FileOutputStream(keystoreFilename), keystorePassword.toCharArray()); | ||
| try (FileOutputStream fos = new FileOutputStream(keystoreFilename)) { | ||
| keystore.store(fos, keystorePassword.toCharArray()); | ||
| } | ||
| System.out.println("Added key " + keyName + ", " + keyAlgorithm + ", " + keyMaterialHex); | ||
| } catch (KeyStoreException e) { | ||
| System.err.println("Failed to store key entry to keystore: " + e); |
| SecretKey key = gen.generateKey(); | ||
| assertNotNull(key); | ||
| assertEquals("HmacSHA256", key.getAlgorithm()); | ||
| assertEquals(128, key.getEncoded().length * 8); |
Check warning
Code scanning / CodeQL
Result of multiplication cast to wider type Warning test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 21 hours ago
In general, to fix this class of issue you ensure that at least one operand in the multiplication is a long, so the operation is performed in 64-bit arithmetic and cannot overflow for any values that fit in long. This is done by casting one operand to long before the multiplication, or by using a long literal such as 8L instead of 8.
In this file, there are four multiplications of the form key.getEncoded().length * 8 that are passed into assertEquals calls: at lines 42, 72, 84, and 91. To fix them without changing functionality, we should cast the length to long before multiplying: (long) key.getEncoded().length * 8. This ensures the multiplication is performed in long. The expected values (keyLength or constants like 128/512) are already int and will be widened as needed; we do not need to change them. No additional imports or new methods are required.
| @@ -39,7 +39,7 @@ | ||
| SecretKey key = gen.generateKey(); | ||
| assertNotNull(key); | ||
| assertEquals("AES", key.getAlgorithm()); | ||
| assertEquals(keyLength, (long) key.getEncoded().length * 8); | ||
| assertEquals(keyLength, (long) key.getEncoded().length * 8L); | ||
| } | ||
| } | ||
|
|
||
| @@ -69,7 +69,7 @@ | ||
| SecretKey key = gen.generateKey(); | ||
| assertNotNull(key); | ||
| assertEquals(algName, key.getAlgorithm()); | ||
| assertEquals(keyLength, (long) key.getEncoded().length * 8); | ||
| assertEquals(keyLength, (long) key.getEncoded().length * 8L); | ||
| } | ||
| } | ||
| } | ||
| @@ -81,14 +81,14 @@ | ||
| SecretKey key = gen.generateKey(); | ||
| assertNotNull(key); | ||
| assertEquals("HmacSHA256", key.getAlgorithm()); | ||
| assertEquals(128, key.getEncoded().length * 8); | ||
| assertEquals(128, (long) key.getEncoded().length * 8L); | ||
|
|
||
| gen = KeyGenerator.getInstance("hmacsha256"); | ||
| gen.init(512); | ||
| key = gen.generateKey(); | ||
| assertNotNull(key); | ||
| assertEquals("HmacSHA256", key.getAlgorithm()); | ||
| assertEquals(512, (long) key.getEncoded().length * 8); | ||
| assertEquals(512, (long) key.getEncoded().length * 8L); | ||
|
|
||
| // HmacSHA224 fails Java 7 but ok with Java 8 | ||
| String[] invalidHmacs = new String[] {"HMAC_SHA256", "HMAC-SHA256"}; |
| long startTime = System.currentTimeMillis(); | ||
| List<String> keyIds = kmipClient.locateAllKeyIds(); | ||
| long time = System.currentTimeMillis() - startTime; | ||
| assertTrue(keyIds.size() >= 0); |
Check warning
Code scanning / CodeQL
Useless comparison test Warning test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 21 hours ago
To fix the problem, we should remove the always‑true comparison and replace it with an assertion that actually checks something meaningful about the returned list. Common sensible checks in tests like this are that the list is not null, and optionally that its size is greater than zero if the environment is expected to contain at least one key.
The minimal, non‑behavior‑changing improvement is to assert that the list is not null. This preserves the current acceptance of empty lists (size 0) while converting the vacuous assertion into one that can fail if locateAllKeyIds() returns null. Concretely:
- In
testLocateAllKeyIds, replaceassertTrue(keyIds.size() >= 0);withassertNotNull(keyIds);. - In
testGetAllKeys, replaceassertTrue(keys.size() >= 0);withassertNotNull(keys);.
No new imports or helper methods are required because assertNotNull is already statically imported at the top of the file.
| @@ -39,7 +39,7 @@ | ||
| long startTime = System.currentTimeMillis(); | ||
| List<String> keyIds = kmipClient.locateAllKeyIds(); | ||
| long time = System.currentTimeMillis() - startTime; | ||
| assertTrue(keyIds.size() >= 0); | ||
| assertNotNull(keyIds); | ||
| System.out.println("Time to locate " + keyIds.size() + " keys = " + time + " ms."); | ||
| for (String id : keyIds) { | ||
| System.out.println("keyId = " + id); | ||
| @@ -63,7 +63,7 @@ | ||
| long startTime = System.currentTimeMillis(); | ||
| List<KmcKey> keys = kmipClient.getAllKeys(); | ||
| long time = System.currentTimeMillis() - startTime; | ||
| assertTrue(keys.size() >= 0); | ||
| assertNotNull(keys); | ||
| System.out.println("Time to retrieve attributes of " + keys.size() + " keys = " + time + " ms."); | ||
| for (KmcKey key : keys) { | ||
| System.out.println("key = " + key); |
| long startTime = System.currentTimeMillis(); | ||
| List<KmcKey> keys = kmipClient.getAllKeys(); | ||
| long time = System.currentTimeMillis() - startTime; | ||
| assertTrue(keys.size() >= 0); |
Check warning
Code scanning / CodeQL
Useless comparison test Warning test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 21 hours ago
In general, to fix this kind of problem you should either remove the always‑true comparison or replace it with an assertion that reflects the real intent of the test (for example, checking that the collection is not empty when that is expected). Keeping an always‑true assertion adds no value and may hide the lack of real verification.
For this specific case in KeyRetrievalTest.testGetAllKeys, the simplest and safest fix without changing existing functionality is to remove the assertion assertTrue(keys.size() >= 0);. The test will still call kmipClient.getAllKeys(), measure and print timing, and iterate over the result as before. We avoid assuming that the list should be non‑empty, because we have no information about the environment or how many keys ought to exist; asserting non‑emptiness could make the test brittle. The only code change needed is to delete line 66 within testGetAllKeys in kmc-key-client/src/test/java/gov/nasa/jpl/ammos/kmc/keyclient/test/KeyRetrievalTest.java. No new methods, imports, or definitions are required.
| @@ -63,7 +63,6 @@ | ||
| long startTime = System.currentTimeMillis(); | ||
| List<KmcKey> keys = kmipClient.getAllKeys(); | ||
| long time = System.currentTimeMillis() - startTime; | ||
| assertTrue(keys.size() >= 0); | ||
| System.out.println("Time to retrieve attributes of " + keys.size() + " keys = " + time + " ms."); | ||
| for (KmcKey key : keys) { | ||
| System.out.println("key = " + key); |
| long startTime = System.currentTimeMillis(); | ||
| List<KmcKey> keys = kmipClient.getAllKeys(); | ||
| long time = System.currentTimeMillis() - startTime; | ||
| assertTrue(keys.size() >= 0); |
Check warning
Code scanning / CodeQL
Container size compared to zero Warning test
| public final void setAttributes(final List<Attribute> attributes) { | ||
| for (Attribute a : attributes) { | ||
| KMIPAttributeValue[] values = a.getValues(); | ||
| if (a instanceof Name) { |
Check notice
Code scanning / CodeQL
Chain of 'instanceof' tests Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 21 hours ago
In general, to fix long instanceof chains when you cannot modify the underlying type hierarchy, you can replace them with a dispatch mechanism like a Map<Class<?>, Handler> that maps runtime types to handler functions. This concentrates all type-specific behavior in a single, extensible structure and avoids having scattered instanceof chains.
For this code, the best targeted fix is to refactor the body of setAttributes so that it does not use a chained if/else if with instanceof. Instead, we can introduce a private static Map<Class<? extends Attribute>, AttributeProcessor> that associates each handled Attribute subclass with a lambda that knows how to extract and assign the corresponding KmcKey field and log it. Then setAttributes simply looks up the processor by a.getClass() and invokes it if present. This keeps behavior identical: same fields set, same logging, and no changes to external interfaces, but significantly improves extensibility and removes the explicit chain.
Concretely:
- Add a private functional interface
AttributeProcessorinsideKmcKey. - Add a private static final
Map<Class<? extends Attribute>, AttributeProcessor> ATTRIBUTE_PROCESSORSinitialized with entries forName,ObjectType,State,CryptographicUsageMask,InitialDate,LastChangeDate, andContactInformation. Each lambda will cast theAttribute, readKMIPAttributeValue[] values = a.getValues();, and set the same fields and logs as before. - Rewrite
setAttributesto iterate overattributes, obtain the corresponding processor from the map withATTRIBUTE_PROCESSORS.get(a.getClass()), and, if non-null, call it; otherwise, ignore unknown attributes (matching current behavior, which simply does nothing for other types).
All changes are local to KmcKey.java within the shown region; no new imports or external dependencies are needed because we use only existing types and standard Java constructs.
| state = ((EnumState) values[0].getValueAsKMIPType()).getKey(); | ||
| logger.debug("setAttributes(); state = " + state); | ||
| } else if (a instanceof CryptographicUsageMask) { | ||
| usageMask = Integer.parseInt(values[0].getValueString()); |
Check notice
Code scanning / CodeQL
Missing catch of NumberFormatException Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 21 hours ago
In general, the fix is to surround the Integer.parseInt call with a try/catch that catches NumberFormatException and handles invalid numeric data gracefully. Handling can include logging the bad value, keeping the previous value, or assigning a safe default, depending on how the rest of the code uses usageMask.
For this specific code, the best minimal‑impact fix is inside setAttributes: when encountering a CryptographicUsageMask, keep the existing parsing logic but catch NumberFormatException. On failure, log an error (including the bad string value) and avoid changing usageMask so that any earlier/default value remains in effect. This preserves current behavior for valid data, adds robustness for invalid data, and does not change the method signature or other logic. All changes are within KmcKey.java around line 157; no new imports are needed because NumberFormatException is in java.lang.
| @@ -154,8 +154,13 @@ | ||
| state = ((EnumState) values[0].getValueAsKMIPType()).getKey(); | ||
| logger.debug("setAttributes(); state = " + state); | ||
| } else if (a instanceof CryptographicUsageMask) { | ||
| usageMask = Integer.parseInt(values[0].getValueString()); | ||
| logger.debug("setAttributes(); usageMask = " + usageMask); | ||
| try { | ||
| usageMask = Integer.parseInt(values[0].getValueString()); | ||
| logger.debug("setAttributes(); usageMask = " + usageMask); | ||
| } catch (NumberFormatException e) { | ||
| logger.error("setAttributes(); invalid CryptographicUsageMask value: '" | ||
| + values[0].getValueString() + "'", e); | ||
| } | ||
| } else if (a instanceof InitialDate) { | ||
| initialDate = values[0].getValueString(); | ||
| logger.debug("setAttributes(); initialDate = " + initialDate); |
| /** | ||
| * Sets the key to the specified state. | ||
| * @param keyRef KeyRef is only used in error messages. It doesn't affect the operation. | ||
| * @param operation The operation to be performed on the key. |
Check notice
Code scanning / CodeQL
Spurious Javadoc @param tags Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 21 hours ago
In general, to fix spurious Javadoc @param tag errors, ensure that each @param name exactly matches a method parameter or type parameter name, and remove or correct any tags that refer to non-existent or misspelled parameters.
For this specific case in KmcKmipKeyClient.java, the method signature is:
public final KmcKey setKeyState(final String keyRef, final String state, final String revokeReason)but the Javadoc says:
/**
* Sets the key to the specified state.
* @param keyRef KeyRef is only used in error messages. It doesn't affect the operation.
* @param operation The operation to be performed on the key.
* @param revokeReason The reason to revoke the key.
* @throws KmcKeyClientException if the operation failed.
*/We should change @param operation to @param state so that the Javadoc correctly documents the second parameter. The descriptive text "The operation to be performed on the key." can remain as-is or be slightly adjusted, but it is not necessary for the CodeQL fix; only the parameter name must match. No new imports or additional methods are needed; this is a pure comment change confined to the shown snippet.
| @@ -833,7 +833,7 @@ | ||
| /** | ||
| * Sets the key to the specified state. | ||
| * @param keyRef KeyRef is only used in error messages. It doesn't affect the operation. | ||
| * @param operation The operation to be performed on the key. | ||
| * @param state The operation to be performed on the key. | ||
| * @param revokeReason The reason to revoke the key. | ||
| * @throws KmcKeyClientException if the operation failed. | ||
| */ |
|


Reverts #57 - incorrectly removed key client as part of removing crypto client