java:S3329 false positive on local variable

Product: SonarQube Community (self-hosted)
sonar-java version: sonar-java 8.28.0.43176 on SonarQube Community Build 26.4.0.121862
Java source level: 21 (javac 21, source/target 17)

Rule

java:S3329 — Cipher IVs must be unpredictable

Description

S3329 inconsistently handles Cipher.DECRYPT_MODE depending on whether it is passed directly to cipher.init(...) or through a local variable. Both forms are semantically identical, but the rule appears to rely on syntactic matching and fails to perform trivial constant propagation.

Reproducer

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.GeneralSecurityException;

public class CipherDecryptExample {
    private static final byte[] KEY_BYTES = new byte[16];
    private static final byte[] IV_BYTES  = new byte[16];

    // BEFORE — no S3329 raised
    public byte[] decryptA(byte[] ct) throws GeneralSecurityException {
        SecretKeySpec ks = new SecretKeySpec(KEY_BYTES, "AES");
        IvParameterSpec iv = new IvParameterSpec(IV_BYTES);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, ks, iv);   // not flagged
        return cipher.doFinal(ct);
    }

    // AFTER — S3329 raised on cipher.init(...)
    public byte[] decryptB(byte[] ct) throws GeneralSecurityException {
        SecretKeySpec ks = new SecretKeySpec(KEY_BYTES, "AES");
        IvParameterSpec iv = new IvParameterSpec(IV_BYTES);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        int opMode = Cipher.DECRYPT_MODE;
        cipher.init(opMode, ks, iv);                // flagged: S3329
        return cipher.doFinal(ct);
    }
}