@@ -143,6 +143,34 @@ check_provider_logs() {
143143 fi
144144}
145145
146+ # Run a single client connection without pcap or key assertions.
147+ run_client () {
148+ local proto=" $1 " cp=" $2 " provider=" $3 "
149+ docker run --network ssl-secrets --rm \
150+ -v $ROOT :/project -v $SECRETS_VOLUME :/secrets \
151+ ssl-secrets-server java -cp " $cp " \
152+ -Djavax.net.ssl.trustStoreType=jks \
153+ -Djavax.net.ssl.trustStore=/secrets/truststore \
154+ -Djavax.net.ssl.trustStorePassword=password \
155+ -Djdk.tls.client.protocols=$proto \
156+ -Dprovider=$provider \
157+ name.neykov.secrets.TestClient https://ssl-secrets-server/secret.txt
158+ }
159+
160+ assert_has_keys () {
161+ local file=" $1 "
162+ [ -s " $file " ] || { echo " Expected keys in $file but file is empty or absent" >&2 ; return 1; }
163+ }
164+
165+ assert_no_keys () {
166+ local file=" $1 "
167+ if [ -s " $file " ]; then
168+ echo " Expected no keys in $file but file contains:" >&2
169+ cat " $file " >&2
170+ return 1
171+ fi
172+ }
173+
146174# Verify that a captured pcap can be decrypted using the given keylog file.
147175check_decryptable () {
148176 local keyfile=" $1 "
@@ -385,3 +413,132 @@ run_ibm_jdk8_tests() {
385413run_ibm_jdk8_tests
386414
387415docker rm -f ssl-secrets-server
416+
417+ # ══════════════════════════════════════════════════════════════════════════════
418+ # Detach/re-attach test: attach → detach → re-attach lifecycle
419+ # Verifies that secrets stop being logged after detach and resume after
420+ # re-attach. Also exercises the double-attach and detach-without-attach guards.
421+ # Run once on a stable LTS; the detach logic is JVM-version-independent.
422+ # ══════════════════════════════════════════════════════════════════════════════
423+
424+ run_detach_test () {
425+ local java_version=" $1 "
426+
427+ echo -e " \n" \
428+ " =============================================\n" \
429+ " Detach/re-attach - Java $java_version \n" \
430+ " =============================================\n\n"
431+
432+ docker rm -f $( docker ps -qa) 2> /dev/null || true
433+ docker build -f $CWD /Dockerfile.server $CWD -t ssl-secrets-server \
434+ --build-arg JAVA_IMAGE_TAG=$java_version
435+
436+ local cp=" $DEFAULT_CP "
437+ local provider=" JSSE"
438+ local proto=" TLSv1.2"
439+
440+ # Start server without agent.
441+ docker run -d --name ssl-secrets-server --network ssl-secrets \
442+ -v $ROOT :/project \
443+ -v $SECRETS_VOLUME :/secrets \
444+ ssl-secrets-server java -cp " $cp " \
445+ -Dprovider=$provider \
446+ -Dkeystore.file=/secrets/keystore \
447+ name.neykov.secrets.TestServer
448+ wait_for_log ssl-secrets-server " server ready"
449+
450+ # ── Attach ───────────────────────────────────────────────────────────────
451+ rm -f $SECRETS_VOLUME /server.keys
452+ docker exec ssl-secrets-server java -jar /project/$JAR_PATH 1 /secrets/server.keys
453+ check_provider_logs " $provider " " attach"
454+
455+ # ── 1. Secrets captured after attach ─────────────────────────────────────
456+ run_client " $proto " " $cp " " $provider "
457+ assert_has_keys $SECRETS_VOLUME /server.keys
458+
459+ # ── 2. Double-attach guard: second attach logs "Already attached" ─────────
460+ docker exec ssl-secrets-server java -jar /project/$JAR_PATH 1 /secrets/server.keys
461+ wait_for_log ssl-secrets-server " Already attached"
462+
463+ # ── 3. Detach: secrets NOT captured for subsequent connections ────────────
464+ docker exec ssl-secrets-server java -jar /project/$JAR_PATH detach 1
465+ wait_for_log ssl-secrets-server " Successfully detached agent"
466+
467+ rm -f $SECRETS_VOLUME /server.keys
468+ run_client " $proto " " $cp " " $provider "
469+ assert_no_keys $SECRETS_VOLUME /server.keys
470+
471+ # ── 4. Detach-without-attach guard: second detach logs "Not attached" ─────
472+ docker exec ssl-secrets-server java -jar /project/$JAR_PATH detach 1
473+ wait_for_log ssl-secrets-server " Not attached"
474+
475+ # ── 5. Re-attach: secrets captured again ─────────────────────────────────
476+ docker exec ssl-secrets-server java -jar /project/$JAR_PATH 1 /secrets/server.keys
477+ rm -f $SECRETS_VOLUME /server.keys
478+ run_client " $proto " " $cp " " $provider "
479+ assert_has_keys $SECRETS_VOLUME /server.keys
480+ }
481+
482+ run_detach_test 21
483+
484+ docker rm -f ssl-secrets-server
485+
486+ # ══════════════════════════════════════════════════════════════════════════════
487+ # Premain-detach test: -javaagent load → runtime detach lifecycle
488+ # Verifies that an agent loaded via -javaagent: can be detached at runtime.
489+ # The Instrumentation object passed to premain is different from the one
490+ # passed to agentmain; this exercises the cross-instance removeTransformer
491+ # + retransformClasses path.
492+ # ══════════════════════════════════════════════════════════════════════════════
493+
494+ run_premain_detach_test () {
495+ local java_version=" $1 "
496+
497+ echo -e " \n" \
498+ " =============================================\n" \
499+ " Premain detach - Java $java_version \n" \
500+ " =============================================\n\n"
501+
502+ docker rm -f $( docker ps -qa) 2> /dev/null || true
503+ docker build -f $CWD /Dockerfile.server $CWD -t ssl-secrets-server \
504+ --build-arg JAVA_IMAGE_TAG=$java_version
505+
506+ local cp=" $DEFAULT_CP "
507+ local provider=" JSSE"
508+ local proto=" TLSv1.2"
509+
510+ # Start server with -javaagent (premain path).
511+ rm -f $SECRETS_VOLUME /server.keys
512+ docker run -d --name ssl-secrets-server --network ssl-secrets \
513+ -v $ROOT :/project \
514+ -v $SECRETS_VOLUME :/secrets \
515+ ssl-secrets-server java -cp " $cp " \
516+ -Dprovider=$provider \
517+ -Dkeystore.file=/secrets/keystore \
518+ -javaagent:/project/$JAR_PATH =/secrets/server.keys \
519+ name.neykov.secrets.TestServer
520+ wait_for_log ssl-secrets-server " server ready"
521+ check_provider_logs " $provider " " agent"
522+
523+ # ── 1. Secrets captured with premain agent ────────────────────────────────
524+ run_client " $proto " " $cp " " $provider "
525+ assert_has_keys $SECRETS_VOLUME /server.keys
526+
527+ # ── 2. Runtime detach stops capture ──────────────────────────────────────
528+ docker exec ssl-secrets-server java -jar /project/$JAR_PATH detach 1
529+ wait_for_log ssl-secrets-server " Successfully detached agent"
530+
531+ rm -f $SECRETS_VOLUME /server.keys
532+ run_client " $proto " " $cp " " $provider "
533+ assert_no_keys $SECRETS_VOLUME /server.keys
534+
535+ # ── 3. Re-attach resumes capture ─────────────────────────────────────────
536+ docker exec ssl-secrets-server java -jar /project/$JAR_PATH 1 /secrets/server.keys
537+ rm -f $SECRETS_VOLUME /server.keys
538+ run_client " $proto " " $cp " " $provider "
539+ assert_has_keys $SECRETS_VOLUME /server.keys
540+ }
541+
542+ run_premain_detach_test 21
543+
544+ docker rm -f ssl-secrets-server
0 commit comments