Claude Skill

sota-jvm

State-of-the-art JVM engineering rules (2026 baseline) for Java and Kotlin that Claude applies when writing or auditing JVM code. Baseline Java 25 LTS (virtual threads final since 21; structured concurrency still preview), Kotlin 2.x. Covers modern idioms (records, sealed types,

LLM Mart · 0 points · 9 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download martinholovsky-SOTA-skills-skills_sota-jvm-ec2abf6.zip · 21 KB
Part of martinholovsky/sota-skills — 39 skills

Install

skills CLI npx skills add https://github.com/martinholovsky/SOTA-skills/tree/main/skills/sota-jvm
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install martinholovsky-sota-skills@llmmart
Git git clone https://github.com/martinholovsky/SOTA-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole martinholovsky/sota-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

SOTA JVM — Java & Kotlin (2026)

Expert-level rules for producing and auditing production JVM code. The JVM is memory-safe (no buffer overflows/UAF), so the risk shifts to deserialization and injection RCE, concurrency correctness, and dependency supply chain. Baseline: Java 25 LTS (records, sealed types, pattern matching, virtual threads finalized in 21 via JEP 444; scoped values finalized in 25 via JEP 506; structured concurrency is still preview — JEP 505 in 25 — don't present it as final), Kotlin 2.x. Per-language idioms differ; shared concerns (the JMM, the JCA, the build/ supply-chain story) are unified here. Every rule states the why; every rules file ends with an audit checklist of grep/analyzer patterns.

Purpose

Two consumers, one source of truth:

  • BUILD mode — generating Java/Kotlin: follow the rules as defaults. Prefer immutability, null-safety, and the standard concurrency primitives. Deviate only with a comment justifying it.
  • AUDIT mode — reviewing existing code: hunt violations with the audit checklists, classify by severity, report in the finding format below. Deserialization of untrusted data and string-built queries are presumed exploitable.

BUILD mode

  1. Before writing, read the rules files relevant to the task (see index). A web service handling untrusted input + threads + a DB needs 02, 03, 04.
  2. Apply the top-10 non-negotiables (below) unconditionally.
  3. New projects: target the current LTS (Java 25), Maven or Gradle with a lockfile, Error Prone + NullAway (Java) or detekt + ktlint (Kotlin), SpotBugs/Find-Sec-Bugs, OWASP dependency-check/OSV-Scanner, and CI running all of it from day one (rules/06).
  4. Prefer immutability (records, final, Kotlin val/data class, unmodifiable collections) and the type system (sealed hierarchies, no raw types, Optional/Kotlin nullable types) over runtime checks.
  5. Never let untrusted bytes reach a deserializer, an XML parser with DTDs on, a JNDI lookup, or a string-built query/EL expression (rules/04).
  6. When you must use a sharp tool (reflection, ObjectInputStream, Unsafe, a @SuppressWarnings), leave a // NOTE(sota): comment explaining why and what bounds it.

AUDIT mode

Work each relevant rules file's audit checklist against the target. Run the greps and analyzers (SpotBugs/Find-Sec-Bugs, Error Prone, detekt); confirm hits manually. Check the dependency tree against known-CVE databases.

Severity conventions

Severity Meaning Examples
CRITICAL Exploitable on reachable input ObjectInputStream.readObject on untrusted data, JNDI lookup of attacker URL (Log4Shell), SpEL/OGNL/ScriptEngine eval of input, SQL via string concat, XXE with DTD enabled
HIGH Likely incident or security weakness Runtime.exec/ProcessBuilder with a shell + interpolation, missing TLS verification, MessageDigest MD5/SHA-1 or Cipher ECB/DES for security, SecureRandom seeded predictably, synchronized+blocking pinning a carrier thread under load (JDK 21–23 only; fixed in 24+ via JEP 491)
MEDIUM Correctness/maintainability hazard Data race on shared mutable state, equals without hashCode, mutable static state, swallowed exceptions, Kotlin platform-type NPE, resource not in try-with-resources/use
LOW Idiom/perf debt Mutable collections returned from APIs, raw types, Optional fields/params, needless boxing on hot path, synchronized where j.u.c fits
INFO Style/doc/hygiene Formatting, naming, missing @Override/@Nullable annotations

Finding format

[SEVERITY] File.java:LINE — short title
  Rule: rules/NN-name.md § section
  Evidence: the offending line(s), verbatim
  Impact: one sentence — what executes/leaks/races, under what input
  Fix: concrete replacement code or action
  Effort: trivial | small | medium | large

Group findings by severity, CRITICAL first. End with: counts per severity, the three highest-leverage fixes, and which checklists/analyzers were run.

Rules index

File Read this when...
rules/01-idioms.md Writing/reviewing any Java/Kotlin: records, sealed types, pattern matching, switch expressions, text blocks, var; Kotlin null-safety, data/value classes, when, scope functions, immutability, Java↔Kotlin interop, error handling
rules/02-design-api.md Designing types/APIs: nullability discipline (Optional, OptionalInt, @Nullable, Kotlin types, platform types), in-band sentinels (absence encoded as -1/0/"") and the JDK's documented -1, immutability, equals/hashCode/toString, exceptions (checked vs unchecked, Kotlin), AutoCloseable/try-with-resources/use, JPMS/package layout, DI
rules/03-concurrency.md Anything with threads, executors, or shared state: virtual threads and pinning, structured concurrency (preview), ExecutorService, CompletableFuture, the Java Memory Model (volatile/final/happens-before), concurrent collections, Kotlin coroutines/structured concurrency/cancellation
rules/04-security.md Any input crossing a trust boundary: Java deserialization + gadget chains + serialization filters, XXE, JNDI/LDAP (Log4Shell-class), expression-language/ScriptEngine injection, SQL/Runtime.exec, path traversal, JCA crypto (AES-GCM, SecureRandom, no MD5/ECB), TLS, secrets; SEI CERT Oracle Java + OWASP
rules/05-performance.md Latency/throughput/memory work: GC choice (G1 default, Generational ZGC for low pause, Parallel for throughput), heap/-Xmx and container awareness, JIT/tiered/-XX basics, JFR + async-profiler, allocation/escape analysis, GraalVM native image trade-offs, JMH benchmarking
rules/06-build-tooling-ci.md Setting up or auditing build/CI: Maven vs Gradle, dependency locking + supply chain (dependency-check/OSV-Scanner, signing, reproducible builds, SBOM), Error Prone/NullAway, SpotBugs/Find-Sec-Bugs, PMD, ktlint/detekt, spotless, JaCoCo. Test strategy lives in sota-testing; this file owns JVM build/test mechanics (JUnit 5, Testcontainers wiring).

Top-10 non-negotiables

  1. Never deserialize untrusted data with Java native serialization. ObjectInputStream.readObject on attacker-controlled bytes is RCE via gadget chains. Use JSON/protobuf with a schema; if unavoidable, apply a strict ObjectInputFilter allowlist (JEP 290). (rules/04)
  2. No string-built queries or commands. Parameterized JDBC/JPA only; ProcessBuilder with an argument list and no shell. String concat into SQL/HQL/JPQL/LDAP/OS commands is CRITICAL. (rules/04)
  3. XML parsers disable DTDs/external entities; no JNDI lookups or EL/script eval of input. XXE, Log4Shell-class JNDI, and SpEL/OGNL/ScriptEngine injection are CRITICAL. (rules/04)
  4. Shared mutable state is correctly synchronized. Respect the JMM: publish via volatile/final/j.u.c; prefer immutability and concurrent collections over hand-rolled locking. A data race is a real bug, not a nondeterministic annoyance. (rules/03)
  5. Virtual threads for blocking I/O concurrency (Java 21+); don't pin. On JDK 21–23 synchronized around blocking calls pins the carrier (use ReentrantLock); JDK 24+ removes most pinning (JEP 491). Never pool virtual threads. (rules/03)
  6. Resources are closed deterministically — try-with-resources (Java) or use {} (Kotlin) for everything AutoCloseable. A leak on the exception path is the default failure of manual finally. (rules/02)
  7. Crypto uses the JCA correctly. AES-GCM (not ECB/CBC-without-MAC), SecureRandom (never Random/Math.random) for keys/tokens/IVs, no MD5/SHA-1 for security, constant-time compare for secrets. Don't roll your own. (rules/04)
  8. Null is designed, not hoped. Kotlin: lean on non-null types, guard platform types from Java; Java: Optional for return values (not fields/ params), @Nullable/NullAway to make nullness checked. (rules/01, rules/02)
  9. Exceptions are handled or propagated, never swallowed. No empty catch; don't catch Exception/Throwable to hide errors; preserve the cause when wrapping. (rules/01, rules/02)
  10. Dependencies are locked, scanned, and minimal. Lockfile committed, OWASP dependency-check/OSV-Scanner gates CI, transitive CVEs triaged, plugins/artifacts from trusted repos. (rules/06)
Files (sota-skills)
  • rules
    • 01-idioms.md 4.7 KB
      # 01 — Idioms: modern Java & Kotlin, error handling
      
      Write to the current language, not Java 8. The through-line is **immutability,
      the type system, and expression-oriented code**. References:
      [Java 25 docs](https://docs.oracle.com/en/java/javase/25/),
      [Kotlin docs](https://kotlinlang.org/docs/home.html), Effective Java (Bloch).
      
      ## 1. Modern Java idioms (21/25)
      
      - **Records** for immutable data carriers — auto `equals`/`hashCode`/`toString`,
        final fields. Add compact constructors for validation. Replace hand-written
        POJOs and most "value" classes.
      - **Sealed** interfaces/classes + **pattern matching for `switch`** for closed
        hierarchies — the compiler enforces exhaustiveness, no `default` needed.
        Replaces visitor boilerplate and instanceof-cascades.
      - `switch` *expressions* (arrow form, yields a value) over fall-through
        statements; record deconstruction patterns in `switch`/`instanceof`.
      - **Text blocks** (`"""`) for multi-line literals; `var` for local inference
        where the type is obvious (not for public API). Streams for transformation,
        but a plain loop when it's clearer.
      - Prefer `List.of`/`Map.of`/`toList()` (immutable) over mutable collections you
        return from APIs.
      
      ## 2. Modern Kotlin idioms (2.x)
      
      - Null-safety is the headline feature: prefer non-null types; use `?`, `?.`,
        `?:` (Elvis), and `requireNotNull`/`checkNotNull` at boundaries. Avoid `!!`
        (it's an assertion that throws) except where you've truly proven non-null.
      - `data class` for value types; `val` over `var`; `when` (exhaustive over
        sealed/enum) over if-chains; immutable collections (`listOf`/`mapOf`) by
        default.
      - Scope functions (`let`/`run`/`apply`/`also`/`with`) for null-safe transforms
        and configuration — but don't over-nest them into write-only code.
      - Extension functions over utility classes; `sealed`/`enum` + `when` for state;
        `object` for singletons; `companion object` for factories.
      - `value class` (inline class) for type-safe wrappers without allocation
        overhead. `Result<T>` / sealed result types for expected failures.
      
      ## 3. Java ↔ Kotlin interop
      
      - **Platform types** (`String!`) are the #1 interop NPE source: a Java method
        with no nullability annotation is seen by Kotlin as "could be null but
        unchecked." Treat Java return values as nullable at the boundary, or annotate
        the Java side with JSpecify `@Nullable`/`@NonNull` (the standard — Kotlin 2
        translates JSpecify to its nullability) so Kotlin enforces it.
      - Annotate Java APIs consumed by Kotlin; use `@JvmStatic`/`@JvmOverloads`/
        `@JvmName` when exposing Kotlin to Java. Kotlin `data class` `copy`/
        destructuring won't appear in Java — design the cross-language surface
        deliberately.
      
      ## 4. Error handling
      
      - **Unchecked exceptions** for programming errors and most application errors;
        reserve checked exceptions for recoverable conditions the caller must handle
        (and they don't exist in Kotlin — all exceptions are unchecked).
      - Never swallow: an empty `catch {}` or `catch (Exception e) {}` that drops the
        error is a MEDIUM–HIGH finding. Catch the narrowest type; rethrow or wrap
        preserving the cause (`new XException("...", e)`); log-or-throw, not both.
      - Don't use exceptions for control flow. For *expected* failures prefer a typed
        return: Java sealed result or `Optional`; Kotlin `Result<T>`/sealed class.
      - Kotlin: don't catch `CancellationException` and swallow it in coroutines (it
        breaks structured cancellation — `rules/03`). Use `runCatching` judiciously,
        not as a blanket swallow.
      
      ## 5. Immutability and finality
      
      - Default to immutable: records, `final` fields/vars, Kotlin `val`, unmodifiable
        collection views. Immutable objects are inherently thread-safe (`rules/03`).
      - Mark classes not designed for inheritance `final` (Java) — Kotlin classes are
        final by default (`open` to allow). Favor composition over inheritance.
      
      ## Audit checklist
      
      ```bash
      # Kotlin !! (non-null assertion) — MEDIUM (latent NPE)
      grep -rnE '!!' --include='*.kt' . | grep -v '!!='
      
      # Swallowed exceptions — MEDIUM/HIGH
      grep -rnzoE 'catch *\([^)]*\) *\{\s*\}' --include='*.java' --include='*.kt' .
      grep -rnE 'catch *\((Exception|Throwable)' --include='*.java' --include='*.kt' .
      
      # Legacy idioms — LOW
      grep -rnE 'new (ArrayList|HashMap|HashSet)<>\(\)' --include='*.java' .   # consider List.of / records
      grep -rnE '\braw\b|new Vector|new Hashtable' --include='*.java' .
      grep -rn 'Optional<' --include='*.java' . | grep -iE 'private .*Optional|(Optional<[^>]+>) [a-z]+\)'  # Optional field/param
      
      # Mutable returns / collections from APIs — LOW
      grep -rnE 'return (this\.)?[a-zA-Z]*[Ll]ist;' --include='*.java' .   # verify defensive copy / unmodifiable
      
      # Analyzer enforcement
      #   Error Prone + NullAway (Java); detekt + ktlint (Kotlin)
      ```
      
    • 02-design-api.md 6.7 KB
      # 02 — API, nullability, immutability, exceptions, resources
      
      Good JVM APIs make illegal states unrepresentable and lifecycles explicit. This
      file covers type/API design; idioms are in `01`, concurrency in `03`.
      
      ## 1. Nullability is part of the type
      
      - **Kotlin**: the type system carries it — design APIs in non-null types,
        accept/return `T?` only where absence is meaningful. Guard Java platform
        types (`rules/01`) at the boundary with `requireNotNull`.
      - **Java**: there is no built-in nullable type, so make it *checked*:
        - `Optional<T>` as a **return** type for "maybe absent" (Bloch: not for
          fields, parameters, or collections — an empty collection is the absence).
        - Annotate with `@Nullable`/`@NonNull` (JSpecify is the standard — adopted
          portfolio-wide by Spring Framework 7 / Boot 4, which deprecated Spring's
          own `org.springframework.lang` annotations) and enforce with
          **NullAway**/Error Prone so violations fail the build.
        - Never return `null` for a collection/array — return empty.
      - Validate arguments at public-method entry (`Objects.requireNonNull`,
        `require`/`check` in Kotlin) and fail fast with a clear message.
      
      ## 1a. In-band sentinels: `-1` is not absence
      
      The JDK returns `-1` for "not found" throughout `String` — `indexOf`, `lastIndexOf`,
      and their `int`/`String`/`fromIndex` overloads all document *"or -1 if there is no
      such occurrence"* (Java SE 21 API docs). Documented and idiomatic there; the class
      (`sota-architecture` rules/02 §8a) is what happens when *your* API does it undocumented.
      
      - §1's rule is the answer for references: absence is `null` with an annotation, or
        `Optional<T>` on a return. For primitives use `OptionalInt`/`OptionalLong`/
        `OptionalDouble` or a boxed `Integer` — not a magic `int`.
      - Kotlin: `Int?` costs a box and is still the right call over `-1`; `indexOf`
        returning `-1` is inherited from the JDK, so wrap it (`indexOfOrNull`) rather than
        letting `-1` travel.
      - Never `Optional` on a **field** or a parameter (it is not `Serializable`, and it
        adds a state); `Optional` is a return-type idiom. That constraint is why
        primitives on fields so often regress to a sentinel — use a boxed type and
        `@Nullable` instead.
      - Audit: `grep -rnE 'return -1;' --include='*.java' --include='*.kt' src/` for
        producers, then comparisons where one operand is `-1`-filtered and the other is not.
      
      ## 2. Immutability and value semantics
      
      - Prefer immutable types (records, `final` fields, Kotlin `data class` with
        `val`). Immutable = thread-safe and cache-friendly.
      - Return defensive copies or unmodifiable views (`List.copyOf`,
        `Collections.unmodifiableList`, Kotlin read-only `List`) — never hand out a
        reference to internal mutable state. (Note Kotlin read-only types are not
        *immutable*, just a read-only view; the backing list can still change.)
      - Builders for objects with many optional fields (or Kotlin named/default
        args, which remove most builder needs).
      
      ## 3. equals / hashCode / toString
      
      - Override them as a set or none. Records and Kotlin `data class` generate a
        consistent set — prefer them over hand-written equality.
      - Hand-written `equals` must be reflexive/symmetric/transitive/consistent and
        match `hashCode`; an object used as a `HashMap` key with `equals` but no
        `hashCode` (or mutated after insertion) silently misbehaves (MEDIUM).
      - Don't put mutable fields in `equals`/`hashCode` if the object is a map key.
      
      ## 4. Exceptions in the API contract
      
      - Document thrown exceptions (`@throws`); throw the most specific standard type
        (`IllegalArgumentException`, `IllegalStateException`,
        `UnsupportedOperationException`) before inventing one.
      - Don't declare `throws Exception`; don't leak implementation exceptions across
        an abstraction boundary — translate to the layer's exception, preserving the
        cause.
      - Checked exceptions only for recoverable conditions the caller must act on;
        overuse pushes callers to swallow. Kotlin has none — document failure modes.
      
      ## 5. Resource lifecycle
      
      - Everything `AutoCloseable`/`Closeable` is acquired in **try-with-resources**
        (Java) or **`use {}`** (Kotlin) — never a bare `close()` in a `finally` you
        can forget on an early return/throw. Multiple resources nest correctly and
        close in reverse order.
      - For pooled resources (DB connections, HTTP clients), return them to the pool
        in the same construct. Don't store a resource you opened in a field without a
        clear close contract and an `AutoCloseable` owner.
      
      ## 6. Module/package structure
      
      - Package by feature, not by layer; keep visibility tight (`private`/
        package-private; Kotlin `internal`). Expose the minimum public surface.
      - **JPMS** (`module-info.java`) for libraries that benefit from strong
        encapsulation and explicit `requires`/`exports`; many apps use the classpath
        with a build-tool module structure instead — choose deliberately.
      - Dependency injection (constructor injection, Spring/Dagger/Koin) over static
        singletons and service locators; mutable static state is a MEDIUM finding
        (testability + concurrency hazard).
      
      ## Audit checklist
      
      ```bash
      # Optional misuse — LOW (field/param/collection)
      grep -rnE '(private|protected|public)\s+Optional<' --include='*.java' .
      grep -rnE '\(.*Optional<[^>]+>\s+\w+\)' --include='*.java' .
      
      # Mutable internal state handed out — MEDIUM
      grep -rnE 'return [a-zA-Z_]+;\s*$' --include='*.java' . | grep -iE 'list|map|set|array'  # verify copy/unmodifiable
      
      # equals without hashCode (and vice versa) — MEDIUM
      grep -rln 'public boolean equals' --include='*.java' . | xargs -I{} sh -c 'grep -L "hashCode" {}'
      
      # Bare close()/no try-with-resources — MEDIUM
      grep -rnE '\.close\(\)' --include='*.java' --include='*.kt' .   # verify try-with-resources/use
      grep -rn 'finally' --include='*.java' . | grep -i close
      
      # Mutable static state — MEDIUM
      grep -rnE 'static (?!final)[A-Za-z<>]+ [a-z]' --include='*.java' .
      
      # throws Exception / overbroad — LOW/MEDIUM
      grep -rn 'throws Exception' --include='*.java' .
      
      # Deprecated Spring nullability annotations (Spring 7+ is JSpecify) — LOW
      grep -rnE 'org\.springframework\.lang\.(Nullable|NonNull)' --include='*.java' --include='*.kt' .
      
      # Enforcement: Error Prone + NullAway, SpotBugs, detekt
      
      # In-band sentinels (§1a) — `-1` is not absence
      grep -rnE 'return -1;' --include='*.java' --include='*.kt' .    # producer; prefer OptionalInt / Integer + @Nullable
      grep -rnE '(int|long) [a-zA-Z]+ = .*\.(indexOf|lastIndexOf)\(' --include='*.java' .  # result STORED, not tested on the next line
      # Module/package structure (§6) — no probe existed before 2026-08-21
      grep -rn 'module-info.java' --include='*.java' . || echo 'no JPMS module descriptors'
      grep -rnE '^import .*\.(internal|impl)\.' --include='*.java' .   # reaching into another package's internals
      ```
      
    • 03-concurrency.md 5.3 KB
      # 03 — Concurrency: virtual threads, the JMM, j.u.c, coroutines
      
      The JVM is memory-safe but not race-free: the Java Memory Model (JMM) defines
      when one thread's writes are visible to another, and code that ignores it has
      real, nondeterministic bugs. Java 21+ adds virtual threads; Kotlin has
      coroutines. References:
      [Java 25 core docs](https://docs.oracle.com/en/java/javase/25/core/),
      [Kotlin coroutines](https://kotlinlang.org/docs/coroutines-overview.html).
      
      ## 1. The Java Memory Model essentials
      
      - A **data race** (two threads access the same field, ≥1 writes, no
        happens-before) yields undefined visibility — a thread may see a stale value
        forever. Fix by establishing happens-before, not by hoping.
      - Establish ordering via: `synchronized`/`ReentrantLock`, `volatile` (visibility
        + ordering for a single field, no compound atomicity), `final` fields (safe
        publication after construction), or `java.util.concurrent` types (which carry
        the guarantees).
      - `volatile` gives visibility but **not** atomic compound actions
        (`count++` on a volatile is still a race) — use `AtomicInteger`/`LongAdder`.
      - Prefer immutability (`rules/01`) and confinement; the cheapest safe sharing is
        no shared mutable state.
      
      ## 2. Prefer high-level concurrency utilities
      
      - Use `java.util.concurrent`: `ExecutorService`/`ThreadPoolExecutor`,
        `ConcurrentHashMap`, `BlockingQueue`, `CompletableFuture`, `CountDownLatch`,
        `Semaphore`, atomics. Don't hand-roll wait/notify or lock protocols.
      - `CompletableFuture` for async composition (`thenCompose`/`thenCombine`);
        always supply an explicit executor and handle `exceptionally`/`handle` —
        default common-pool + swallowed exceptions is a trap.
      - Lock with try/finally or prefer `ReentrantLock` with `lock()`/`unlock()` in
        finally; keep critical sections small; acquire multiple locks in a global
        order to avoid deadlock.
      
      ## 3. Virtual threads (Java 21+, finalized JEP 444)
      
      - Virtual threads make thread-per-request with blocking I/O scale — millions of
        cheap threads scheduled by the JVM. Use them for I/O-bound concurrency:
        `Executors.newVirtualThreadPerTaskExecutor()`.
      - **Don't pool virtual threads** (they're cheap; pooling defeats the point).
      - **Pinning is version-conditional**: on JDK 21–23 a `synchronized` block/
        method around a *blocking* call pins the carrier thread — HIGH under load;
        use `ReentrantLock`. Since JDK 24 (JEP 491) monitors no longer pin except
        native frames (JNI/FFM callbacks) and class initializers — keep the
        `ReentrantLock` advice only for code that must support 21 LTS. Avoid heavy
        `ThreadLocal` use.
      - CPU-bound work still wants a bounded platform-thread pool sized to cores.
      - **Scoped values** (`ScopedValue`) are **final in Java 25** (JEP 506) — safe
        to recommend as GA. **Structured concurrency** (`StructuredTaskScope`) is
        still *preview* (JEP 505 in 25; previews continue in later JDKs) — use
        behind a preview flag, note it's not yet stable, and don't recommend it as
        GA.
      
      ## 4. Kotlin coroutines
      
      - Coroutines are structured by default: launch in a `CoroutineScope` tied to a
        lifecycle; child coroutines are cancelled with the parent. Never use
        `GlobalScope` (unstructured leak).
      - Pick the right dispatcher: `Dispatchers.IO` for blocking I/O,
        `Dispatchers.Default` for CPU work, `Main` for UI. `withContext` to switch.
      - **Cooperative cancellation**: check `isActive`/`ensureActive()` or use
        cancellable suspend funcs; never catch-and-swallow `CancellationException`
        (rethrow it). Use `withTimeout` for deadlines.
      - Don't block a coroutine thread (`Thread.sleep`, blocking JDBC) without
        `Dispatchers.IO`; prefer suspending APIs. `Flow` for async streams with
        backpressure.
      
      ## 5. Tooling
      
      - Run concurrent tests deterministically where possible; use jcstress for
        low-level memory-model tests, and stress/load tests for races. SpotBugs flags
        some concurrency bugs (e.g. inconsistent synchronization); Error Prone has
        `@GuardedBy` checking.
      
      ## Audit checklist
      
      ```bash
      # Data-race smells — MEDIUM/HIGH (verify happens-before)
      grep -rnE '\bstatic (?!final)[A-Za-z<>\[\]]+ [a-z]' --include='*.java' .   # mutable shared static
      grep -rnE 'volatile ' --include='*.java' . | grep -E '\+\+|--|\+='          # compound op on volatile = race
      grep -rn 'HashMap\|ArrayList' --include='*.java' . | grep -i 'static\|shared'  # non-concurrent shared coll
      
      # Virtual-thread pitfalls
      grep -rn 'newVirtualThreadPerTaskExecutor\|Thread.ofVirtual' --include='*.java' .
      grep -rnE 'synchronized' --include='*.java' . | grep -i 'block\|io\|http\|jdbc'  # pinning: HIGH on JDK 21–23; non-issue on 24+ (JEP 491) except native/class-init frames
      grep -rn 'preview' --include='*.java' .   # structured concurrency is preview in 25
      
      # CompletableFuture without executor/exception handling — MEDIUM
      grep -rnE 'CompletableFuture\.(supplyAsync|runAsync)\([^,)]*\)' --include='*.java' .   # no explicit executor
      
      # Kotlin coroutine hazards — MEDIUM/HIGH
      grep -rn 'GlobalScope' --include='*.kt' .                       # unstructured leak
      grep -rnE 'catch *\([^)]*CancellationException' --include='*.kt' .  # must rethrow
      grep -rnE 'runBlocking|Thread.sleep' --include='*.kt' .         # blocking in coroutine context
      
      # Bare lock without finally — MEDIUM
      grep -rnE '\.lock\(\)' --include='*.java' --include='*.kt' .    # verify unlock in finally
      ```
      
    • 04-security.md 8.3 KB
      # 04 — Security: deserialization, injection, XXE, JNDI, crypto
      
      The JVM removes memory-corruption bugs, so the dominant RCE classes are
      **unsafe deserialization, injection, and lookup/eval of untrusted data**, plus
      crypto misuse. Treat every byte from network/file/DB/IPC as hostile.
      Standards: [SEI CERT Oracle Java](https://wiki.sei.cmu.edu/confluence/display/java),
      [OWASP Deserialization](https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html),
      [OWASP Java](https://cheatsheetseries.owasp.org/).
      
      ## 1. Deserialization — the signature Java RCE
      
      - **Never** call `ObjectInputStream.readObject()` on untrusted bytes. Gadget
        chains in common libraries turn deserialization into arbitrary code execution
        (the entire `ysoserial` family). This is CRITICAL on sight.
      - Prefer data formats with no code-execution semantics: JSON/protobuf/Avro with
        an explicit schema, deserialized into known DTOs. Disable polymorphic type
        handling unless allowlisted (Jackson `enableDefaultTyping`/`@JsonTypeInfo`
        with untrusted input is the JSON equivalent of the gadget problem).
      - If native serialization is unavoidable, install a strict **`ObjectInputFilter`**
        allowlist (JEP 290, `setObjectInputFilter` / `jdk.serialFilter`) limiting
        classes and graph size. Treat it as defense-in-depth, not a fix.
      - **Framework surfaces deserialize too** — gadget entry points are shifting from
        direct `readObject` to message converters and persisted state. Spring JMS
        `MappingJackson2MessageConverter` instantiated attacker-chosen classes from
        message type headers (CVE-2026-41855; fix adds `setTrustedPackages(...)`), and
        Spring Statemachine's Kryo persistence lacked a class allowlist
        (CVE-2026-41862). Require an explicit type allowlist on any converter or
        persistence layer that resolves classes from data.
      
      ## 2. Injection (SQL, command, LDAP, expression)
      
      - **SQL/JPQL/HQL**: parameterized `PreparedStatement` / bound JPA parameters
        only. String concatenation into a query is CRITICAL — no exceptions for
        "internal" values. `ORDER BY`/identifiers can't be bound: allowlist them.
      - **OS command**: `ProcessBuilder` with an argument **list** and no shell; never
        `Runtime.exec("sh -c " + input)`. Validate/allowlist the program.
        **`Runtime.getRuntime().exec(String)` tokenizes its argument on whitespace** — it is
        the `shell:true` of Java, and one tainted value carrying a space becomes two
        arguments. The JDK agrees: the three `String`-taking overloads are
        `@Deprecated(since="18", forRemoval=false)`; the `String[]` ones are not (read off
        `Runtime.class` on Temurin 25.0.3, 2026-08-20). Any surviving `exec(String)` call is
        a finding on the deprecation alone — grep below.
        **Deadlines**: `waitFor(t, unit)` returns `false` on timeout and fires on schedule
        (2003 ms on a 2 s budget, measured) — but it kills **nothing**, and `destroy()` reaps
        only the direct child, orphaning any grandchild holding the inherited pipe. Java is
        the one mainstream runtime with a portable fix: `p.descendants().forEach(
        ProcessHandle::destroyForcibly)` before `p.destroyForcibly()` (Java 9+, verified to
        kill the grandchild). Cross-language comparison: `sota-sandboxing` rules/04 R5.3a.
      - **LDAP/JNDI**: never pass attacker-controlled names to `Context.lookup` —
        this is the Log4Shell (CVE-2021-44228) class. Disable remote-codebase loading;
        validate URLs against an allowlist; keep logging libs patched.
      - **Expression/script eval**: SpEL, OGNL, MVEL, `ScriptEngine` (Nashorn/JS),
        Spring expression contexts, and template engines evaluating user input are
        RCE. Don't evaluate untrusted expressions; sandbox or remove the capability.
      
      ## 3. XML and XXE
      
      - Disable DTDs and external entities on every parser
        (`DocumentBuilderFactory`, `SAXParserFactory`, `XMLInputFactory`,
        transformers):
        `setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)`,
        disable `external-general-entities`/`external-parameter-entities`,
        `setXIncludeAware(false)`, `setExpandEntityReferences(false)` (OWASP XXE
        cheat sheet). Same care for YAML (`SnakeYAML` `SafeConstructor`) and XML-based
        formats.
      
      ## 4. Cryptography (the JCA)
      
      - Use vetted algorithms via the JCA; **don't roll your own**.
        - Symmetric: AES-256 in an authenticated mode (**GCM**); never ECB, never
          unauthenticated CBC. Unique random nonce per message.
        - Randomness: **`SecureRandom`** for keys/tokens/IVs/salts — never
          `java.util.Random`/`Math.random`/`ThreadLocalRandom`. Don't seed
          `SecureRandom` with a fixed value.
        - Hashing: SHA-256+ for integrity; **password hashing** uses Argon2/bcrypt/
          PBKDF2 (a KDF), never plain SHA/MD5. MD5/SHA-1 for security is HIGH.
        - Constant-time comparison for MACs/tokens (`MessageDigest.isEqual`), never
          `String.equals`/`Arrays.equals` on secrets (timing leak).
      - TLS: use the platform default protocols/cipher suites (TLS 1.2+/1.3); **never**
        install an all-trusting `TrustManager` or `HostnameVerifier` that returns
        true — disabling certificate validation is HIGH/CRITICAL. See
        `sota-code-security` rules/04 and `sota-network-security`.
      
      ## 5. Other boundaries
      
      - **Path traversal**: canonicalize and verify the result stays under an allowed
        root (`Path.normalize()` + `startsWith`); reject `..`. Use `java.nio.file`.
      - **SSRF / URL fetch**: validate/allowlist destinations; block internal/metadata
        ranges (see `sota-code-security`).
      - **Secrets**: never hardcode; load from a secret manager/env; don't log them;
        prefer `char[]`/`byte[]` you can wipe over `String` for passwords (`rules`
        cross-ref `sota-secrets-management`).
      - **Spring/framework**: keep dependencies patched (Spring4Shell, Log4Shell were
        dependency CVEs — `rules/06`); enable CSRF/auth correctly (`sota-code-security`).
      - **`assert` is not a control**: assertions are **disabled by default** at
        runtime — Oracle's own guide says so, and adds that once disabled they are
        "essentially equivalent to empty statements in semantics and performance".
        Production JVMs are rarely started with `-ea`, so a validation or bounds check
        written as `assert` is a no-op in the deployment while reading correct in
        source. Use an explicit `if` + throw (or `Objects.requireNonNull`,
        `Preconditions`-style checks that survive). Class:
        `sota-code-security` rules/11 §4.
      
      ## Audit checklist
      
      ```bash
      # Deserialization — CRITICAL
      grep -rnE 'readObject\(|ObjectInputStream|XMLDecoder' --include='*.java' .
      grep -rnE 'enableDefaultTyping|@JsonTypeInfo|activateDefaultTyping' --include='*.java' .  # Jackson polymorphic
      grep -rnE 'MappingJackson2MessageConverter|JacksonJsonMessageConverter|new Kryo\(' --include='*.java' --include='*.kt' .  # framework deser — verify type allowlist
      
      # Injection — CRITICAL/HIGH
      grep -rnE '(createQuery|createNativeQuery|prepareStatement|executeQuery|executeUpdate)\([^?)]*\+' --include='*.java' .
      grep -rnE 'Runtime\.getRuntime\(\)\.exec|new ProcessBuilder' --include='*.java' --include='*.kt' .
      # The String-taking exec overloads TOKENIZE on whitespace and are @Deprecated(since=18):
      # a hit here is a finding on the deprecation alone, before any taint analysis.
      grep -rnE 'Runtime\.getRuntime\(\)\.exec\(\s*"' --include='*.java' --include='*.kt' .
      # waitFor(t,unit) reaps nothing: a destroy() with no descendants() sweep orphans grandchildren
      grep -rn 'waitFor(' --include='*.java' --include='*.kt' . | grep -v 'descendants'
      grep -rnE 'ctx\.lookup|InitialContext|new InitialDirContext' --include='*.java' .   # JNDI/Log4Shell-class
      grep -rnE 'SpelExpressionParser|Ognl|ScriptEngineManager|getEngineByName' --include='*.java' .
      
      # XXE — CRITICAL (verify DTDs disabled)
      grep -rnE 'DocumentBuilderFactory|SAXParserFactory|XMLInputFactory|TransformerFactory|SAXReader' --include='*.java' .
      grep -rn 'disallow-doctype-decl\|setExpandEntityReferences\|SafeConstructor' --include='*.java' . || echo "verify XXE hardening"
      
      # Crypto misuse — HIGH
      grep -rnE 'new Random\(|Math\.random|ThreadLocalRandom' --include='*.java' . | grep -iE 'key|token|iv|salt|nonce|secret'
      grep -rnE '"(MD5|SHA-?1|DES|RC4)"|/ECB/|Cipher\.getInstance\("AES"\)' --include='*.java' --include='*.kt' .
      grep -rnE 'TrustManager|HostnameVerifier|checkServerTrusted' --include='*.java' .   # all-trusting?
      grep -rn 'Arrays.equals\|\.equals(' --include='*.java' . | grep -iE 'mac|hmac|token|signature|digest'
      
      # Static security analysis
      #   SpotBugs + Find-Sec-Bugs; OWASP dependency-check / OSV-Scanner (rules/06)
      ```
      
    • 05-performance.md 4.5 KB
      # 05 — Performance: GC, JIT, profiling, allocation, native image
      
      JVM performance is mostly about **garbage collection, allocation rate, and
      letting the JIT warm up** — not micro-tweaks. Measure with a profiler and a
      proper benchmark harness; never tune GC flags by guess. Cross-reference
      `sota-performance` for methodology. [Java 25 perf docs](https://docs.oracle.com/en/java/javase/25/).
      
      ## 1. Garbage collectors — pick by goal
      
      - **G1** (default) — balanced throughput/latency; good for most server apps.
      - **Generational ZGC** (`-XX:+UseZGC` — generational-only since JDK 24, JEP
        490; the `ZGenerational` flag is obsolete and will eventually make the JVM
        refuse to start) — sub-millisecond pauses for large heaps / latency-sensitive
        services; slightly lower peak throughput.
      - **Parallel** — max throughput for batch jobs where pause time doesn't matter.
      - Set `-Xmx`/`-Xms` deliberately; in containers rely on container-awareness
        (`-XX:+UseContainerSupport`, default on) or `-XX:MaxRAMPercentage` rather than
        hardcoding — and verify the JVM sees the cgroup limit. Don't tune collectors
        before profiling shows GC is the bottleneck.
      
      ## 2. JIT and warmup
      
      - HotSpot interprets then JIT-compiles hot code (tiered C1→C2). Benchmarks and
        latency SLOs must account for **warmup** — cold p99 is not steady-state.
      - Don't prematurely "optimize" in source for the JIT (it inlines, escapes-
        analyzes, devirtualizes). Write clear code; let C2 work. Megamorphic call
        sites (many implementations behind one interface) defeat inlining — relevant
        only when profiled.
      - For fast startup/low footprint (serverless, CLIs): consider **AppCDS**/
        class-data sharing, or **GraalVM Native Image** (§5).
      
      ## 3. Allocation is the usual cost
      
      - Allocation rate drives GC frequency. Reduce churn on hot paths: reuse buffers,
        avoid needless boxing (`Integer` vs `int`, autoboxing in loops/collections —
        prefer primitive specializations / `IntStream`), avoid per-call temporary
        collections and string concatenation in loops (`StringBuilder`).
      - Escape analysis can stack-allocate non-escaping objects — keep short-lived
        objects local. Avoid finalizers and excessive `ThreadLocal` (esp. with
        virtual threads, `rules/03`).
      - Right-size collections (initial capacity) to avoid resize churn.
      
      ## 4. Profiling — measure, don't guess
      
      - **JDK Flight Recorder (JFR)** — low-overhead, always-on-capable profiling of
        allocation, locks, GC, I/O; analyze in JDK Mission Control. The default first
        tool for production.
      - **async-profiler** — low-overhead CPU/alloc/lock flame graphs without the
        safepoint bias of older samplers.
      - Benchmark microbenchmarks with **JMH** (handles warmup, dead-code
        elimination, fork isolation) — never `System.nanoTime()` around a loop. Report
        distributions (`sota-performance`).
      
      ## 5. GraalVM Native Image (trade-offs)
      
      - Ahead-of-time compiles to a native binary: fast startup, low memory — great
        for serverless/CLI/short-lived workloads. Costs: closed-world assumption means
        **reflection/proxies/resources need configuration** (or framework support —
        Spring AOT, Quarkus, Micronaut), longer build, and lower peak throughput than
        a warmed-up JIT for long-running compute.
      - Choose native image for startup/footprint-bound services; stay on the JIT for
        long-running throughput-bound ones. Test the native binary — behavior can
        differ from JVM mode.
      
      ## Audit checklist
      
      ```bash
      # Allocation/boxing on hot paths — LOW/MEDIUM (verify with profiler)
      grep -rnE '\+ ?"' --include='*.java' . | grep -iE 'for|while|loop'        # string concat in loops
      grep -rnE 'new (Integer|Long|Double|Boolean)\(' --include='*.java' .       # boxing / deprecated ctors
      grep -rnE 'List<Integer>|Map<Integer,|Map<.*,Integer>' --include='*.java' . # boxing-heavy collections
      
      # GC/heap flags sane and container-aware?
      grep -rnE 'Xmx|Xms|MaxRAMPercentage|UseZGC|UseG1GC|UseParallelGC' Dockerfile* k8s/ deploy/ *.sh 2>/dev/null
      grep -rn 'ZGenerational' Dockerfile* k8s/ deploy/ *.sh 2>/dev/null   # obsolete since JDK 24 (JEP 490)
      grep -rn 'UseContainerSupport' . 2>/dev/null
      
      # Benchmark hygiene — verify JMH, not nanoTime loops
      grep -rn 'System.nanoTime\|currentTimeMillis' --include='*.java' . | grep -i bench
      grep -rln '@Benchmark' --include='*.java' . || echo "no JMH benchmarks"
      
      # Native image config present if used?
      grep -rn 'native-image\|GraalVM\|reflect-config\|reachability-metadata' . 2>/dev/null
      
      # Profile first: JFR (-XX:StartFlightRecording) or async-profiler — no static grep
      ```
      
    • 06-build-tooling-ci.md 4 KB
      # 06 — Build, tooling, supply chain, CI
      
      JVM project safety lives in the build: dependency locking and CVE scanning,
      static analysis, and consistent CI gates. This file owns build/test
      *mechanics*; test **strategy** (suite shape, doubles, coverage philosophy)
      lives in `sota-testing`.
      
      ## 1. Build tool: Maven or Gradle
      
      - **Maven** — declarative, stable, ubiquitous; predictable for libraries and
        most services. **Gradle** — flexible, faster incremental builds, Kotlin DSL;
        pick it for complex/multi-module or Android-adjacent builds. Either is fine;
        consistency and reproducibility matter more than the choice.
      - Pin the build-tool version (Maven Wrapper `mvnw` / Gradle Wrapper `gradlew`
        with a checksum) so every machine and CI uses the same version.
      - Target the current LTS (Java 25) via `release`/toolchains; set
        `--release N` (not just `-source`/`-target`) so you don't accidentally use
        newer APIs on an older bytecode target.
      
      ## 2. Dependency management & supply chain
      
      - **Lock dependencies**: Gradle dependency locking / version catalogs; Maven
        via the dependency-management section + a reproducible resolution (or the
        `maven-lockfile`-style plugins). Pin versions; avoid version ranges and
        `latest`. Commit the lock state.
      - **Scan for known CVEs in CI**: OWASP **dependency-check** or **OSV-Scanner**
        (or Snyk/GitHub Dependabot) gating the build; triage transitive CVEs. Log4Shell
        and Spring4Shell were dependency CVEs — this gate is non-negotiable.
      - Resolve only from trusted repositories (Maven Central / your mirror) over
        HTTPS; verify signatures/checksums; beware dependency confusion (don't let an
        internal coordinate resolve from a public repo). Generate an **SBOM**
        (CycloneDX) for releases. See `sota-devsecops`.
      - Minimize the tree — each transitive dep is attack surface and a future CVE.
      
      ## 3. Static analysis & formatting
      
      - **Error Prone** (+ **NullAway** for null-safety) on the Java compile — catches
        real bugs at build time; treat as errors in CI.
      - **SpotBugs** + **Find-Sec-Bugs** for bug/security patterns (incl. crypto,
        injection, deserialization sinks from `rules/04`); **PMD** for additional
        rules.
      - **Kotlin**: **detekt** (static analysis) + **ktlint** (style); both in CI.
      - **spotless** (or google-java-format/ktlint) to enforce formatting in CI
        (`--check`) so style never enters review.
      
      ## 4. CI gates
      
      - A PR build runs: compile with `-Werror`-equivalent (Error Prone as error),
        unit + integration tests, SpotBugs/detekt, dependency CVE scan, coverage
        (**JaCoCo**) with a threshold, and format check. Fail the build on any.
      - **JUnit 5** is the standard runner; **Testcontainers** for real-dependency
        integration tests (DB/broker) — wire them here; *strategy* is `sota-testing`.
        Run with a fixed timezone/locale/seed for determinism.
      - Build reproducibly: `-Dproject.build.outputTimestamp` / Gradle reproducible
        archives; pin plugin versions.
      
      ## Audit checklist
      
      ```bash
      # Wrapper pinned? LTS targeted?
      ls mvnw gradlew 2>/dev/null | grep -q . || echo "no build wrapper (version not pinned)"
      grep -rnE 'release|sourceCompatibility|targetCompatibility|languageVersion' pom.xml build.gradle* 2>/dev/null
      
      # Dependency CVE scan + locking in CI?
      grep -rniE 'dependency-check|osv-scanner|dependabot|snyk|cyclonedx' .github/ pom.xml build.gradle* 2>/dev/null \
        || echo "no dependency CVE scan — HIGH"
      ls gradle.lockfile gradle/dependency-locks 2>/dev/null; grep -rn 'dependencyLocking' build.gradle* 2>/dev/null
      grep -rnE 'version ranges|\[.*,.*\)|latest\.release|\+' build.gradle* 2>/dev/null   # unpinned ranges
      
      # Static analysis configured?
      grep -rniE 'errorprone|nullaway|spotbugs|findsecbugs|pmd|detekt|ktlint|spotless' \
        . --include='pom.xml' --include='build.gradle*' --include='*.yml' || echo "no static analysis configured"
      
      # Coverage gate + JUnit5/Testcontainers?
      grep -rniE 'jacoco|junit-jupiter|testcontainers' pom.xml build.gradle* 2>/dev/null
      
      # Repository over HTTPS, trusted only
      grep -rnE 'http://|maven \{|repositories' pom.xml build.gradle* settings.* 2>/dev/null | grep -i 'http://'
      ```
      
  • SKILL.md 9.4 KB
    ---
    name: sota-jvm
    description: >-
      State-of-the-art JVM engineering rules (2026 baseline) for Java and Kotlin
      that Claude applies when writing or auditing JVM code. Baseline Java 25 LTS
      (virtual threads final since 21; structured concurrency still preview),
      Kotlin 2.x. Covers modern idioms (records, sealed types, pattern matching,
      Kotlin null-safety/coroutines), API/null/immutability design, concurrency
      (virtual threads, JMM, java.util.concurrent, coroutines), security
      (deserialization/gadget chains, XXE, JNDI/Log4Shell-class, injection, JCA
      crypto; SEI CERT Oracle Java + OWASP), performance (G1/ZGC, JFR, GraalVM),
      and build/tooling/CI (Maven/Gradle, dependency-check, Error Prone/NullAway,
      SpotBugs, ktlint/detekt). Trigger keywords - Java, Kotlin, JVM, JDK,
      Spring, record, sealed, virtual thread, Loom, coroutine, suspend,
      ObjectInputStream, deserialization, XXE, JNDI, Log4Shell, Maven, Gradle,
      G1, ZGC, GraalVM, JMH, Optional, null-safety. Use for BOTH building JVM
      services/libraries and reviewing or auditing them.
    ---
    
    # SOTA JVM — Java & Kotlin (2026)
    
    Expert-level rules for producing and auditing production JVM code. The JVM is
    memory-safe (no buffer overflows/UAF), so the risk shifts to **deserialization
    and injection RCE, concurrency correctness, and dependency supply chain**.
    Baseline: **Java 25 LTS** (records, sealed types, pattern matching, virtual
    threads finalized in 21 via JEP 444; scoped values finalized in 25 via JEP 506;
    structured concurrency is still *preview* — JEP 505 in 25 — don't present it
    as final), **Kotlin 2.x**.
    Per-language idioms differ; shared concerns (the JMM, the JCA, the build/
    supply-chain story) are unified here. Every rule states the *why*; every rules
    file ends with an audit checklist of grep/analyzer patterns.
    
    ## Purpose
    
    Two consumers, one source of truth:
    
    - **BUILD mode** — generating Java/Kotlin: follow the rules as defaults. Prefer
      immutability, null-safety, and the standard concurrency primitives. Deviate
      only with a comment justifying it.
    - **AUDIT mode** — reviewing existing code: hunt violations with the audit
      checklists, classify by severity, report in the finding format below.
      Deserialization of untrusted data and string-built queries are presumed
      exploitable.
    
    ## BUILD mode
    
    1. Before writing, read the rules files relevant to the task (see index). A web
       service handling untrusted input + threads + a DB needs `02`, `03`, `04`.
    2. Apply the **top-10 non-negotiables** (below) unconditionally.
    3. New projects: target the current LTS (Java 25), Maven or Gradle with a
       lockfile, Error Prone + NullAway (Java) or detekt + ktlint (Kotlin),
       SpotBugs/Find-Sec-Bugs, OWASP dependency-check/OSV-Scanner, and CI running
       all of it from day one (`rules/06`).
    4. Prefer immutability (records, `final`, Kotlin `val`/`data class`,
       unmodifiable collections) and the type system (sealed hierarchies, no raw
       types, `Optional`/Kotlin nullable types) over runtime checks.
    5. Never let untrusted bytes reach a deserializer, an XML parser with DTDs on,
       a JNDI lookup, or a string-built query/EL expression (`rules/04`).
    6. When you must use a sharp tool (reflection, `ObjectInputStream`, `Unsafe`,
       a `@SuppressWarnings`), leave a `// NOTE(sota):` comment explaining why and
       what bounds it.
    
    ## AUDIT mode
    
    Work each relevant rules file's audit checklist against the target. Run the
    greps and analyzers (SpotBugs/Find-Sec-Bugs, Error Prone, detekt); confirm hits
    manually. Check the dependency tree against known-CVE databases.
    
    ### Severity conventions
    
    | Severity | Meaning | Examples |
    |---|---|---|
    | **CRITICAL** | Exploitable on reachable input | `ObjectInputStream.readObject` on untrusted data, JNDI lookup of attacker URL (Log4Shell), SpEL/OGNL/`ScriptEngine` eval of input, SQL via string concat, XXE with DTD enabled |
    | **HIGH** | Likely incident or security weakness | `Runtime.exec`/`ProcessBuilder` with a shell + interpolation, missing TLS verification, `MessageDigest` MD5/SHA-1 or `Cipher` ECB/`DES` for security, `SecureRandom` seeded predictably, `synchronized`+blocking pinning a carrier thread under load (JDK 21–23 only; fixed in 24+ via JEP 491) |
    | **MEDIUM** | Correctness/maintainability hazard | Data race on shared mutable state, `equals` without `hashCode`, mutable static state, swallowed exceptions, Kotlin platform-type NPE, resource not in try-with-resources/`use` |
    | **LOW** | Idiom/perf debt | Mutable collections returned from APIs, raw types, `Optional` fields/params, needless boxing on hot path, `synchronized` where `j.u.c` fits |
    | **INFO** | Style/doc/hygiene | Formatting, naming, missing `@Override`/`@Nullable` annotations |
    
    ### Finding format
    
    ```
    [SEVERITY] File.java:LINE — short title
      Rule: rules/NN-name.md § section
      Evidence: the offending line(s), verbatim
      Impact: one sentence — what executes/leaks/races, under what input
      Fix: concrete replacement code or action
      Effort: trivial | small | medium | large
    ```
    
    Group findings by severity, CRITICAL first. End with: counts per severity, the
    three highest-leverage fixes, and which checklists/analyzers were run.
    
    ## Rules index
    
    | File | Read this when... |
    |---|---|
    | `rules/01-idioms.md` | Writing/reviewing any Java/Kotlin: records, sealed types, pattern matching, switch expressions, text blocks, `var`; Kotlin null-safety, `data`/`value` classes, `when`, scope functions, immutability, Java↔Kotlin interop, error handling |
    | `rules/02-design-api.md` | Designing types/APIs: nullability discipline (`Optional`, `OptionalInt`, `@Nullable`, Kotlin types, platform types), **in-band sentinels (absence encoded as `-1`/`0`/`""`)** and the JDK's documented `-1`, immutability, `equals`/`hashCode`/`toString`, exceptions (checked vs unchecked, Kotlin), `AutoCloseable`/try-with-resources/`use`, JPMS/package layout, DI |
    | `rules/03-concurrency.md` | Anything with threads, executors, or shared state: virtual threads and pinning, structured concurrency (preview), `ExecutorService`, `CompletableFuture`, the Java Memory Model (`volatile`/`final`/happens-before), concurrent collections, Kotlin coroutines/structured concurrency/cancellation |
    | `rules/04-security.md` | Any input crossing a trust boundary: Java deserialization + gadget chains + serialization filters, XXE, JNDI/LDAP (Log4Shell-class), expression-language/`ScriptEngine` injection, SQL/`Runtime.exec`, path traversal, JCA crypto (AES-GCM, `SecureRandom`, no MD5/ECB), TLS, secrets; SEI CERT Oracle Java + OWASP |
    | `rules/05-performance.md` | Latency/throughput/memory work: GC choice (G1 default, Generational ZGC for low pause, Parallel for throughput), heap/`-Xmx` and container awareness, JIT/tiered/`-XX` basics, JFR + async-profiler, allocation/escape analysis, GraalVM native image trade-offs, JMH benchmarking |
    | `rules/06-build-tooling-ci.md` | Setting up or auditing build/CI: Maven vs Gradle, dependency locking + supply chain (dependency-check/OSV-Scanner, signing, reproducible builds, SBOM), Error Prone/NullAway, SpotBugs/Find-Sec-Bugs, PMD, ktlint/detekt, spotless, JaCoCo. **Test *strategy* lives in `sota-testing`; this file owns JVM build/test mechanics (JUnit 5, Testcontainers wiring).** |
    
    ## Top-10 non-negotiables
    
    1. **Never deserialize untrusted data with Java native serialization.**
       `ObjectInputStream.readObject` on attacker-controlled bytes is RCE via gadget
       chains. Use JSON/protobuf with a schema; if unavoidable, apply a strict
       `ObjectInputFilter` allowlist (JEP 290). (`rules/04`)
    2. **No string-built queries or commands.** Parameterized JDBC/JPA only;
       `ProcessBuilder` with an argument list and no shell. String concat into
       SQL/HQL/JPQL/LDAP/OS commands is CRITICAL. (`rules/04`)
    3. **XML parsers disable DTDs/external entities; no JNDI lookups or EL/script
       eval of input.** XXE, Log4Shell-class JNDI, and SpEL/OGNL/`ScriptEngine`
       injection are CRITICAL. (`rules/04`)
    4. **Shared mutable state is correctly synchronized.** Respect the JMM: publish
       via `volatile`/`final`/`j.u.c`; prefer immutability and concurrent
       collections over hand-rolled locking. A data race is a real bug, not a
       nondeterministic annoyance. (`rules/03`)
    5. **Virtual threads for blocking I/O concurrency (Java 21+); don't pin.**
       On JDK 21–23 `synchronized` around blocking calls pins the carrier (use
       `ReentrantLock`); JDK 24+ removes most pinning (JEP 491). Never pool
       virtual threads. (`rules/03`)
    6. **Resources are closed deterministically** — try-with-resources (Java) or
       `use {}` (Kotlin) for everything `AutoCloseable`. A leak on the exception
       path is the default failure of manual `finally`. (`rules/02`)
    7. **Crypto uses the JCA correctly.** AES-GCM (not ECB/CBC-without-MAC),
       `SecureRandom` (never `Random`/`Math.random`) for keys/tokens/IVs, no
       MD5/SHA-1 for security, constant-time compare for secrets. Don't roll your
       own. (`rules/04`)
    8. **Null is designed, not hoped.** Kotlin: lean on non-null types, guard
       platform types from Java; Java: `Optional` for return values (not fields/
       params), `@Nullable`/NullAway to make nullness checked. (`rules/01`,
       `rules/02`)
    9. **Exceptions are handled or propagated, never swallowed.** No empty `catch`;
       don't catch `Exception`/`Throwable` to hide errors; preserve the cause when
       wrapping. (`rules/01`, `rules/02`)
    10. **Dependencies are locked, scanned, and minimal.** Lockfile committed,
        OWASP dependency-check/OSV-Scanner gates CI, transitive CVEs triaged,
        plugins/artifacts from trusted repos. (`rules/06`)
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related