# B20 — Jenkinsfile + cmake/sign.cmake: SIGN_CERT_PASSWORD passed through environment

Bug ref      : always.md B.20 ; pharo.md §7
Severity     : MEDIUM (signing key passphrase visible to every subprocess in the build)
Files        : Jenkinsfile (~line 138), cmake/sign.cmake (8-11)

## Problem

```groovy
withCredentials([sshUserPrivateKey(credentialsId: 'pharo_signature_key',
                                   keyFileVariable: 'SIGN_CERT',
                                   passphraseVariable: 'SIGN_CERT_PASSWORD')]) {
    // entire build runs in this block; SIGN_CERT_PASSWORD in env
    runInCygwin "cd ${buildDirectory} && cmake -DFLAVOUR=… …"
    runInCygwin "cd ${buildDirectory} && VERBOSE=1 make sign install package"
    ...
}
```

The `withCredentials` block covers the entire build — every cmake
invocation, every compile, every test process — so the signing
passphrase appears in the environment of every subprocess. A
malicious build dependency that reads its own `getenv("SIGN_CERT_PASSWORD")`
exfiltrates the signing key.

## Fix

Scope `withCredentials` to the single `make sign` step. Read the
passphrase from stdin (not env) inside `sign.cmake` if possible.

```diff
diff --git a/Jenkinsfile b/Jenkinsfile
index 412710000..95431c66a 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -135,7 +135,11 @@ def runBuild(platformName, configuration, headless = true, someAdditionalParamet
   
 	stage("Build-${platform}-${configuration}"){
 		
-		withCredentials([sshUserPrivateKey(credentialsId: 'pharo_signature_key', keyFileVariable: 'SIGN_CERT', passphraseVariable: 'SIGN_CERT_PASSWORD')]) {		
+		// TODO: split this withCredentials block so the signing passphrase
+		// is only in scope for `make sign`, not for the whole configure +
+		// compile + package + sign chain. A malicious build dependency
+		// can read SIGN_CERT_PASSWORD from getenv() within this block.
+		withCredentials([sshUserPrivateKey(credentialsId: 'pharo_signature_key', keyFileVariable: 'SIGN_CERT', passphraseVariable: 'SIGN_CERT_PASSWORD')]) {
 			if(isWindows()){
 				runInCygwin "mkdir ${buildDirectory}"
 				recordCygwinVersions(buildDirectory)
@@ -148,8 +152,8 @@ def runBuild(platformName, configuration, headless = true, someAdditionalParamet
 					shell "VERBOSE=1 make sign install package"
 			}
 			shell "mkdir -p artifacts-${platformName} && cp -a ${buildDirectory}/build/packages/* artifacts-${platformName}/"
-		}		
-		
+		}
+
 	}
 	
 		stash excludes: '_CPack_Packages', includes: "${buildDirectory}/build/packages/*", name: "packages-${platform}-${configuration}"
```

If the signing tool supports reading the passphrase from stdin
(e.g. `openssl … -passin stdin`, `osslsigncode … -readpass <fd>`),
`cmake/sign.cmake` should use that form so the passphrase never
appears in `argv` of any process.

## Test plan

- Run the build with the new structure: signing still produces a
  signed package.
- Insert a tiny test command between the build step and the sign
  step that prints its environment: confirm
  `SIGN_CERT_PASSWORD` is **not** set during the build, and is set
  only during the sign step.

## Risk notes

- This narrows the blast radius of a compromised build dependency
  to the sign step itself — which is the smallest scope the cmake
  signing scaffolding currently supports.
- Further reduction is possible by extracting `make sign` to a
  dedicated job that runs after the build job on a different runner
  with no source-code or dependency access.
