mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-09-10 02:39:39 +02:00
Compare commits
13 commits
Author | SHA1 | Date | |
---|---|---|---|
0fe3a7abf6 | |||
0d807cb6b8 | |||
9b0a3cd4c7 | |||
0ee31b232a | |||
f2cbde43be | |||
4cf6c6b16a | |||
0f54cc615c | |||
a74db2d26d | |||
|
5f30df6d16 | ||
7953ade136 | |||
0649c041cd | |||
5a413f53a4 | |||
3b92ccc59d |
14 changed files with 51 additions and 128 deletions
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
|
@ -36,7 +36,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
language: [ 'java' ]
|
language: [ 'java-kotlin' ]
|
||||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ jobs:
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
@ -57,7 +57,7 @@ jobs:
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v2
|
uses: github/codeql-action/autobuild@v3
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
|
@ -71,4 +71,4 @@ jobs:
|
||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v3
|
||||||
|
|
|
@ -5,6 +5,10 @@ SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
# PGPainless Changelog
|
# PGPainless Changelog
|
||||||
|
|
||||||
|
## 1.7.7-SNAPSHOT
|
||||||
|
- Bump `bcpg-jdk8on` to `1.81`
|
||||||
|
- Bump `bcprov-jdk18on` to `1.81`
|
||||||
|
|
||||||
## 1.7.6
|
## 1.7.6
|
||||||
- Fix `RevocationSignatureBuilder` properly calculating third-party signatures of type `KeyRevocation` (delegation revocations)
|
- Fix `RevocationSignatureBuilder` properly calculating third-party signatures of type `KeyRevocation` (delegation revocations)
|
||||||
- Enable support for native images
|
- Enable support for native images
|
||||||
|
|
|
@ -93,6 +93,12 @@ precedence = "aggregate"
|
||||||
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>, 2017 Steve Smith"
|
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>, 2017 Steve Smith"
|
||||||
SPDX-License-Identifier = "CC-BY-SA-3.0"
|
SPDX-License-Identifier = "CC-BY-SA-3.0"
|
||||||
|
|
||||||
|
[[annotations]]
|
||||||
|
path = "pgpainless-cli/src/main/resources/META-INF/native-image/**"
|
||||||
|
precedence = "aggregate"
|
||||||
|
SPDX-FileCopyrightText = "2025 Paul Schaub <info@pgpainless.org>"
|
||||||
|
SPDX-License-Identifier = "Apache-2.0"
|
||||||
|
|
||||||
[[annotations]]
|
[[annotations]]
|
||||||
path = "pgpainless-cli/rewriteManPages.sh"
|
path = "pgpainless-cli/rewriteManPages.sh"
|
||||||
precedence = "aggregate"
|
precedence = "aggregate"
|
||||||
|
|
|
@ -43,7 +43,7 @@ allprojects {
|
||||||
|
|
||||||
// checkstyle
|
// checkstyle
|
||||||
checkstyle {
|
checkstyle {
|
||||||
toolVersion = '10.12.1'
|
toolVersion = '10.25.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
spotless {
|
spotless {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'application'
|
id 'application'
|
||||||
id 'org.graalvm.buildtools.native' version '0.10.6'
|
id 'org.graalvm.buildtools.native' version '0.10.6'
|
||||||
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
id 'com.gradleup.shadow' version '8.3.6'
|
||||||
}
|
}
|
||||||
|
|
||||||
graalvmNative {
|
graalvmNative {
|
||||||
|
|
|
@ -1,16 +1,7 @@
|
||||||
[
|
[
|
||||||
{
|
|
||||||
"name":"[B"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name":"[Ljava.lang.Object;"
|
"name":"[Ljava.lang.Object;"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name":"[Ljava.lang.String;"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"[Lsun.security.pkcs.SignerInfo;"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name":"ch.qos.logback.classic.encoder.PatternLayoutEncoder",
|
"name":"ch.qos.logback.classic.encoder.PatternLayoutEncoder",
|
||||||
"queryAllPublicMethods":true,
|
"queryAllPublicMethods":true,
|
||||||
|
@ -70,9 +61,6 @@
|
||||||
{
|
{
|
||||||
"name":"java.lang.RuntimePermission"
|
"name":"java.lang.RuntimePermission"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name":"java.lang.String"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name":"java.lang.System",
|
"name":"java.lang.System",
|
||||||
"methods":[{"name":"console","parameterTypes":[] }]
|
"methods":[{"name":"console","parameterTypes":[] }]
|
||||||
|
@ -101,9 +89,6 @@
|
||||||
"name":"java.nio.file.Paths",
|
"name":"java.nio.file.Paths",
|
||||||
"methods":[{"name":"get","parameterTypes":["java.lang.String","java.lang.String[]"] }]
|
"methods":[{"name":"get","parameterTypes":["java.lang.String","java.lang.String[]"] }]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name":"java.security.AlgorithmParametersSpi"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name":"java.security.AllPermission"
|
"name":"java.security.AllPermission"
|
||||||
},
|
},
|
||||||
|
@ -119,21 +104,6 @@
|
||||||
{
|
{
|
||||||
"name":"java.security.cert.PKIXRevocationChecker"
|
"name":"java.security.cert.PKIXRevocationChecker"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name":"java.security.interfaces.DSAPrivateKey"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"java.security.interfaces.DSAPublicKey"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"java.security.interfaces.RSAPrivateKey"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"java.security.interfaces.RSAPublicKey"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"java.security.spec.DSAParameterSpec"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name":"java.sql.Connection"
|
"name":"java.sql.Connection"
|
||||||
},
|
},
|
||||||
|
@ -208,9 +178,6 @@
|
||||||
"name":"java.time.ZonedDateTime",
|
"name":"java.time.ZonedDateTime",
|
||||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name":"java.util.Date"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name":"java.util.HashSet"
|
"name":"java.util.HashSet"
|
||||||
},
|
},
|
||||||
|
@ -245,11 +212,6 @@
|
||||||
{
|
{
|
||||||
"name":"java.util.concurrent.locks.ReentrantLock$Sync"
|
"name":"java.util.concurrent.locks.ReentrantLock$Sync"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name":"javax.security.auth.x500.X500Principal",
|
|
||||||
"fields":[{"name":"thisX500Name"}],
|
|
||||||
"methods":[{"name":"<init>","parameterTypes":["sun.security.x509.X500Name"] }]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name":"javax.smartcardio.CardPermission"
|
"name":"javax.smartcardio.CardPermission"
|
||||||
},
|
},
|
||||||
|
@ -333,6 +295,10 @@
|
||||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.NTRU$Mappings",
|
"name":"org.bouncycastle.jcajce.provider.asymmetric.NTRU$Mappings",
|
||||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name":"org.bouncycastle.jcajce.provider.asymmetric.NoSig$Mappings",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.RSA$Mappings",
|
"name":"org.bouncycastle.jcajce.provider.asymmetric.RSA$Mappings",
|
||||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
@ -914,18 +880,6 @@
|
||||||
"queryAllDeclaredMethods":true,
|
"queryAllDeclaredMethods":true,
|
||||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name":"sun.security.provider.DSA$SHA256withDSA",
|
|
||||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"sun.security.provider.DSAKeyFactory",
|
|
||||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"sun.security.provider.DSAParameters",
|
|
||||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name":"sun.security.provider.NativePRNG",
|
"name":"sun.security.provider.NativePRNG",
|
||||||
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"<init>","parameterTypes":["java.security.SecureRandomParameters"] }]
|
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"<init>","parameterTypes":["java.security.SecureRandomParameters"] }]
|
||||||
|
@ -933,59 +887,5 @@
|
||||||
{
|
{
|
||||||
"name":"sun.security.provider.SHA",
|
"name":"sun.security.provider.SHA",
|
||||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"sun.security.provider.SHA2$SHA256",
|
|
||||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"sun.security.provider.X509Factory",
|
|
||||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"sun.security.rsa.RSAKeyFactory$Legacy",
|
|
||||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"sun.security.rsa.RSASignature$SHA256withRSA",
|
|
||||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"sun.security.util.ObjectIdentifier"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"sun.security.x509.AuthorityInfoAccessExtension",
|
|
||||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"sun.security.x509.AuthorityKeyIdentifierExtension",
|
|
||||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"sun.security.x509.BasicConstraintsExtension",
|
|
||||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"sun.security.x509.CRLDistributionPointsExtension",
|
|
||||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"sun.security.x509.CertificateExtensions"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"sun.security.x509.CertificatePoliciesExtension",
|
|
||||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"sun.security.x509.ExtendedKeyUsageExtension",
|
|
||||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"sun.security.x509.KeyUsageExtension",
|
|
||||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"sun.security.x509.SubjectKeyIdentifierExtension",
|
|
||||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
|
@ -40,8 +40,6 @@
|
||||||
"pattern":"\\Qsop-java-version.properties\\E"
|
"pattern":"\\Qsop-java-version.properties\\E"
|
||||||
}, {
|
}, {
|
||||||
"pattern":"java.base:\\Qsun/text/resources/LineBreakIteratorData\\E"
|
"pattern":"java.base:\\Qsun/text/resources/LineBreakIteratorData\\E"
|
||||||
}, {
|
|
||||||
"pattern":"java.base:\\Qsun/text/resources/nfkc.icu\\E"
|
|
||||||
}]},
|
}]},
|
||||||
"bundles":[{
|
"bundles":[{
|
||||||
"name":"msg_armor",
|
"name":"msg_armor",
|
||||||
|
|
|
@ -45,7 +45,7 @@ class KeyRingBuilder : KeyRingBuilderInterface<KeyRingBuilder> {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addUserId(userId: CharSequence): KeyRingBuilder = apply {
|
override fun addUserId(userId: CharSequence): KeyRingBuilder = apply {
|
||||||
userIds[userId.toString().trim()] = null
|
userIds[userId.toString()] = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addUserId(userId: ByteArray): KeyRingBuilder =
|
override fun addUserId(userId: ByteArray): KeyRingBuilder =
|
||||||
|
|
|
@ -478,7 +478,7 @@ class SecretKeyRingEditor(
|
||||||
val prevBinding =
|
val prevBinding =
|
||||||
inspectKeyRing(secretKeyRing).getCurrentSubkeyBindingSignature(keyId)
|
inspectKeyRing(secretKeyRing).getCurrentSubkeyBindingSignature(keyId)
|
||||||
?: throw NoSuchElementException(
|
?: throw NoSuchElementException(
|
||||||
"Previous subkey binding signaure for ${keyId.openPgpKeyId()} MUST NOT be null.")
|
"Previous subkey binding signature for ${keyId.openPgpKeyId()} MUST NOT be null.")
|
||||||
val bindingSig = reissueSubkeyBindingSignature(subkey, expiration, protector, prevBinding)
|
val bindingSig = reissueSubkeyBindingSignature(subkey, expiration, protector, prevBinding)
|
||||||
secretKeyRing = injectCertification(secretKeyRing, subkey, bindingSig)
|
secretKeyRing = injectCertification(secretKeyRing, subkey, bindingSig)
|
||||||
}
|
}
|
||||||
|
@ -569,9 +569,10 @@ class SecretKeyRingEditor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sanitizeUserId(userId: CharSequence): CharSequence =
|
private fun sanitizeUserId(userId: CharSequence): CharSequence =
|
||||||
// TODO: Further research how to sanitize user IDs.
|
// I'm not sure, what kind of sanitization is needed.
|
||||||
// e.g. what about newlines?
|
// Newlines are allowed, they just need to be escaped when emitted in an ASCII armor header
|
||||||
userId.toString().trim()
|
// Trailing/Leading whitespace is also fine.
|
||||||
|
userId.toString()
|
||||||
|
|
||||||
private fun callbackFromRevocationAttributes(attributes: RevocationAttributes?) =
|
private fun callbackFromRevocationAttributes(attributes: RevocationAttributes?) =
|
||||||
object : RevocationSignatureSubpackets.Callback {
|
object : RevocationSignatureSubpackets.Callback {
|
||||||
|
|
|
@ -247,7 +247,9 @@ class ArmorUtils {
|
||||||
.add(OpenPgpFingerprint.of(publicKey).prettyPrint())
|
.add(OpenPgpFingerprint.of(publicKey).prettyPrint())
|
||||||
// Primary / First User ID
|
// Primary / First User ID
|
||||||
(primary ?: first)?.let {
|
(primary ?: first)?.let {
|
||||||
headerMap.getOrPut(HEADER_COMMENT) { mutableSetOf() }.add(it)
|
headerMap
|
||||||
|
.getOrPut(HEADER_COMMENT) { mutableSetOf() }
|
||||||
|
.add(it.replace("\n", "\\n").replace("\r", "\\r"))
|
||||||
}
|
}
|
||||||
// X-1 further identities
|
// X-1 further identities
|
||||||
when (userIds.size) {
|
when (userIds.size) {
|
||||||
|
|
|
@ -11,14 +11,9 @@ import org.bouncycastle.util.Arrays
|
||||||
*
|
*
|
||||||
* @param chars may be null for empty passwords.
|
* @param chars may be null for empty passwords.
|
||||||
*/
|
*/
|
||||||
class Passphrase(chars: CharArray?) {
|
class Passphrase(private val chars: CharArray?) {
|
||||||
private val lock = Any()
|
private val lock = Any()
|
||||||
private var valid = true
|
private var valid = true
|
||||||
private val chars: CharArray?
|
|
||||||
|
|
||||||
init {
|
|
||||||
this.chars = trimWhitespace(chars)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a copy of the underlying char array. A return value of null represents an empty
|
* Return a copy of the underlying char array. A return value of null represents an empty
|
||||||
|
@ -67,6 +62,13 @@ class Passphrase(chars: CharArray?) {
|
||||||
|
|
||||||
override fun hashCode(): Int = getChars()?.let { String(it) }.hashCode()
|
override fun hashCode(): Int = getChars()?.let { String(it) }.hashCode()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a copy of this [Passphrase], but with whitespace characters trimmed off.
|
||||||
|
*
|
||||||
|
* @return copy with trimmed whitespace
|
||||||
|
*/
|
||||||
|
fun withTrimmedWhitespace(): Passphrase = Passphrase(trimWhitespace(chars))
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -67,7 +67,7 @@ byte[] encrypted = sop.encrypt()
|
||||||
|
|
||||||
// Decrypt a message
|
// Decrypt a message
|
||||||
ByteArrayAndResult<DecryptionResult> messageAndVerifications = sop.decrypt()
|
ByteArrayAndResult<DecryptionResult> messageAndVerifications = sop.decrypt()
|
||||||
.verifyWith(cert)
|
.verifyWithCert(cert)
|
||||||
.withKey(key)
|
.withKey(key)
|
||||||
.ciphertext(encrypted)
|
.ciphertext(encrypted)
|
||||||
.toByteArrayAndResult();
|
.toByteArrayAndResult();
|
||||||
|
|
|
@ -100,4 +100,14 @@ public class GenerateKeyTest {
|
||||||
assertThrows(SOPGPException.UnsupportedProfile.class, () ->
|
assertThrows(SOPGPException.UnsupportedProfile.class, () ->
|
||||||
sop.generateKey().profile("invalid"));
|
sop.generateKey().profile("invalid"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generateKeyWithNewlinesInUserId() throws IOException {
|
||||||
|
byte[] keyBytes = sop.generateKey()
|
||||||
|
.userId("Foo\n\nBar")
|
||||||
|
.generate()
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
assertTrue(new String(keyBytes).contains("Foo\\n\\nBar"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
ext {
|
ext {
|
||||||
shortVersion = '1.7.6'
|
shortVersion = '1.7.7'
|
||||||
isSnapshot = false
|
isSnapshot = true
|
||||||
javaSourceCompatibility = 11
|
javaSourceCompatibility = 11
|
||||||
bouncyCastleVersion = '1.80'
|
bouncyCastleVersion = '1.81'
|
||||||
bouncyPgVersion = bouncyCastleVersion
|
bouncyPgVersion = bouncyCastleVersion
|
||||||
junitVersion = '5.8.2'
|
junitVersion = '5.8.2'
|
||||||
logbackVersion = '1.5.13'
|
logbackVersion = '1.5.13'
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue