@@ -5,6 +5,9 @@ plugins {
55group = ' org.finos.gitproxy'
66version = ' 0.0.1-SNAPSHOT'
77
8+ // Gitleaks version bundled into the JAR for hermetic secret scanning
9+ ext. gitleaksVersion = ' 8.21.2'
10+
811java {
912 sourceCompatibility = JavaVersion . VERSION_21
1013 targetCompatibility = JavaVersion . VERSION_21
@@ -106,3 +109,94 @@ dependencies {
106109tasks. named(' test' ) {
107110 useJUnitPlatform()
108111}
112+
113+ // ---------------------------------------------------------------------------
114+ // Hermetic gitleaks bundling
115+ // ---------------------------------------------------------------------------
116+ // Detects the current build platform (OS + arch) at Gradle configuration time
117+ // and downloads the matching gitleaks binary, storing it as the plain name
118+ // "gitleaks" under build/generated-resources/gitleaks/ so it is packed into
119+ // the JAR as the classpath resource "gitleaks/gitleaks".
120+ //
121+ // At runtime, GitleaksRunner extracts it to a JVM temp directory (cleaned up
122+ // on JVM shutdown) and invokes it directly — no host installation required.
123+ //
124+ // If the build platform is not recognised (e.g. Windows), the download is
125+ // skipped and a warning is printed. In that case set
126+ // commit.secretScanning.scanner-path in git-proxy-local.yml to point at a
127+ // locally installed gitleaks binary.
128+ // ---------------------------------------------------------------------------
129+
130+ // Resource root: files here appear at the root of the classpath in the JAR.
131+ // The gitleaks binary lands at build/generated-resources/gitleaks
132+ // and is packed into the JAR as the classpath resource "gitleaks".
133+ def gitleaksResourceDir = layout. buildDirectory. dir(" generated-resources" )
134+
135+ sourceSets. main. resources. srcDir(gitleaksResourceDir)
136+
137+ // Detect build-time OS and CPU architecture to select the right gitleaks tarball
138+ def buildOs = System . properties[' os.name' ]. toLowerCase()
139+ def buildArch = System . properties[' os.arch' ]. toLowerCase()
140+
141+ def gitleaksTarSuffix = {
142+ String osKey = buildOs. contains(' linux' ) ? ' linux'
143+ : (buildOs. contains(' mac' ) || buildOs. contains(' darwin' )) ? ' darwin'
144+ : null
145+ String archKey = (buildArch. contains(' amd64' ) || buildArch. contains(' x86_64' )) ? ' x64'
146+ : (buildArch. contains(' aarch64' ) || buildArch. contains(' arm64' )) ? ' arm64'
147+ : null
148+ (osKey && archKey) ? " ${ osKey} _${ archKey} " : null
149+ }()
150+
151+ tasks. register(' downloadGitleaks' ) {
152+ group = ' build setup'
153+ description = " Downloads the gitleaks ${ gitleaksVersion} binary for the current build platform."
154+
155+ // Output: build/generated-resources/gitleaks → JAR classpath: gitleaks
156+ def outputFile = gitleaksResourceDir. map { it. file(" gitleaks" ) }
157+ outputs. file(outputFile)
158+
159+ doLast {
160+ if (! gitleaksTarSuffix) {
161+ logger. warn(
162+ " Unsupported build platform (os='${ buildOs} ', arch='${ buildArch} ') — " +
163+ " skipping bundled gitleaks download. Set commit.secretScanning.scanner-path " +
164+ " in git-proxy-local.yml to use a locally installed gitleaks binary." )
165+ return
166+ }
167+
168+ def out = outputFile. get(). asFile
169+ if (out. exists()) {
170+ logger. lifecycle(" gitleaks ${ gitleaksVersion} (${ gitleaksTarSuffix} ) already bundled, skipping." )
171+ return
172+ }
173+ out. parentFile. mkdirs()
174+
175+ def tarName = " gitleaks_${ gitleaksVersion} _${ gitleaksTarSuffix} .tar.gz"
176+ def tarUrl = " https://github.com/gitleaks/gitleaks/releases/download/v${ gitleaksVersion} /${ tarName} "
177+ def tmpDir = layout. buildDirectory. dir(" tmp/gitleaks" ). get(). asFile
178+ tmpDir. mkdirs()
179+ def tarFile = new File (tmpDir, tarName)
180+
181+ logger. lifecycle(" Downloading gitleaks ${ gitleaksVersion} (${ gitleaksTarSuffix} )..." )
182+ new URL (tarUrl). withInputStream { is -> tarFile. bytes = is. bytes }
183+
184+ // Extract just the gitleaks binary; Ant places it as out.parent/gitleaks == out
185+ ant. untar(src : tarFile. absolutePath, dest : out. parent, compression : ' gzip' ) {
186+ patternset { include(name : ' gitleaks' ) }
187+ }
188+
189+ if (! out. exists()) {
190+ throw new GradleException (" gitleaks binary not found in ${ tarName} after extraction" )
191+ }
192+ out. setExecutable(true )
193+ logger. lifecycle(" Bundled gitleaks at ${ out} (${ out.length()} bytes)" )
194+ }
195+ }
196+
197+ // The JAR always ships with the bundled gitleaks binary (DEFAULT_VERSION).
198+ // At runtime, GitleaksRunner uses it unless:
199+ // - commit.secretScanning.scanner-path is set (explicit override), or
200+ // - commit.secretScanning.version is set to a different version and auto-install is true
201+ // (downloads and caches the requested version on first use).
202+ processResources. dependsOn(' downloadGitleaks' )
0 commit comments