in jar-infer/jar-infer-lib/src/main/java/com/uber/nullaway/jarinfer/BytecodeAnnotator.java [225:272]
private static void copyAndAnnotateJarEntry(
JarEntry jarEntry,
InputStream is,
JarOutputStream jarOS,
MethodParamAnnotations nonnullParams,
MethodReturnAnnotations nullableReturns,
String nullableDesc,
String nonnullDesc,
boolean stripJarSignatures)
throws IOException {
String entryName = jarEntry.getName();
if (entryName.endsWith(".class")) {
jarOS.putNextEntry(createZipEntry(jarEntry.getName()));
annotateBytecode(is, jarOS, nonnullParams, nullableReturns, nullableDesc, nonnullDesc);
} else if (entryName.equals("META-INF/MANIFEST.MF")) {
// Read full file
StringBuilder stringBuilder = new StringBuilder();
BufferedReader br = new BufferedReader(new InputStreamReader(is, UTF_8));
String currentLine;
while ((currentLine = br.readLine()) != null) {
stringBuilder.append(currentLine + "\n");
}
String manifestText = stringBuilder.toString();
// Check for evidence of jar signing, note that lines can be split if too long so regex
// matching line by line will have false negatives.
// NOTE: this code only handles the case where the message digest algorithm used when signing
// was SHA-256. Eventually we may need a more robust solution for other digest algorithms.
// E.g., on JDK 21, the default message digest algorithm is SHA-384, and this code does not
// work for that algorithm (the DIGEST_ENTRY_PATTERN regex is hardcoded for SHA-256)
String manifestMinusDigests = manifestText.replaceAll(DIGEST_ENTRY_PATTERN, "");
if (!manifestText.equals(manifestMinusDigests) && !stripJarSignatures) {
throw new SignedJarException(SIGNED_JAR_ERROR_MESSAGE);
}
jarOS.putNextEntry(createZipEntry(jarEntry.getName()));
jarOS.write(manifestMinusDigests.getBytes(UTF_8));
} else if (entryName.startsWith("META-INF/")
&& (entryName.endsWith(".DSA")
|| entryName.endsWith(".RSA")
|| entryName.endsWith(".SF"))) {
if (!stripJarSignatures) {
throw new SignedJarException(SIGNED_JAR_ERROR_MESSAGE);
} // the case where stripJarSignatures==true is handled by default by skipping these files
} else {
jarOS.putNextEntry(createZipEntry(jarEntry.getName()));
jarOS.write(IOUtils.toByteArray(is));
}
jarOS.closeEntry();
}