Skip to content

Cross-platform App Hardening (DexGuard-class), Enterprise-gated - #5527

Open
shai-almog wants to merge 19 commits into
masterfrom
app-hardening
Open

Cross-platform App Hardening (DexGuard-class), Enterprise-gated#5527
shai-almog wants to merge 19 commits into
masterfrom
app-hardening

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

What

Adds App Hardening — a single Enterprise-gated layer that renames classes/methods/fields, encrypts string constants, and obfuscates control flow across every port (Android, iOS/ParparVM, JavaScript, native desktop) from one bytecode transform, integrated with Crash Protection so obfuscated traces are still symbolicated.

Runs on the cloud build server; this repo carries the engine and the client surface. The matching BuildDaemon PR turns it on (entitlement gate + engine invocation + mapping upload).

Pieces

  • maven/cn1-hardening — forked-process ProGuard+ASM engine. Demux → ProGuard rename with a zq-prefixed dictionary that avoids the ParparVM NativeSymbolIndex dead-code-culler pathology → string encryption (LDC and static final ConstantValue, per-class decoder) → opaque-predicate control flow (safe platforms) → mangle-collision guard → CheckClassAdapter verify → mapping with provenance + mappingId. Android keeps R8 as its sole renamer.
  • maven/cn1-retrace — ProGuard mapping parse/chain + the ParparVM trace-string parser that on-device Throwable.getStackTrace() now mirrors, + a local retrace CLI.
  • Crash integrationrawStack/traceFormat/mappingId/hardenLevel payload fields, PiiScrubber.scrubRawStack, cause-chain capture.
  • Surface/entitlementharden.* hints, HardeningPreflight (fails local/source targets, invalid level, on-device-debug), Executor.hardenSourceJar/runBuild, read-only Hardening status API, simulator hint schema, new App-Hardening.asciidoc.
  • Prereq fixes — invalid build_key literal, BuildHintEditor grouped-Select .values lookup, security.asciidoc "obfuscates by default" overclaim, ParparVM getStackTrace() (fixes today's empty-frames iOS crash reports).

Tests

25 unit tests green across the two modules and the crash payload: full ProGuard round-trip + behaviour preservation, string round-trip + plaintext-absence, control-flow verification, mapping retrace + chaining, trace-format detection (incl. V8/SpiderMonkey JS rejection), and the pre-flight truth table. Core, plugin, and javase compile.

Follow-ups (not blocking)

Device end-to-end retrace and the "did obfuscation break the app" matrix need a real cloud build. Android R8 -applymapping/keep export, parparvm-symbols.txt native-address symbolication, daemon runtime stamping of cn1.mappingId/cn1.hardened, HardeningVerifier (Check 3), and SpotBugs-gate wiring for the new modules are noted for later.

Pairs with the BuildDaemon app-hardening PR.

🤖 Generated with Claude Code

Adds a single hardening layer that renames classes/methods/fields, encrypts
string constants and obfuscates control flow across every port (Android, iOS/
ParparVM, JavaScript, native desktop) from one bytecode transform, integrated
with Crash Protection so obfuscated stack traces are still symbolicated.

Engine (new maven/cn1-hardening, run as a forked process so it is single-sourced
with the build daemon and carries its own ProGuard/ASM): demux the fat jar,
rename with ProGuard using a prefixed dictionary that avoids the ParparVM
NativeSymbolIndex culler pathology, encrypt LDC literals and static-final
ConstantValue strings with a per-class decoder, opaque-predicate control flow on
safe platforms, ParparVM mangle-collision guard, CheckClassAdapter verification,
and a cross-platform mapping. Android keeps R8 as its sole renamer.

Symbolication (new maven/cn1-retrace): ProGuard mapping parse/chain plus the
ParparVM trace-string parser that java.lang.Throwable.getStackTrace() now mirrors
on device, and a local retrace CLI. Crash payload gains rawStack/traceFormat/
mappingId/hardenLevel; PiiScrubber.scrubRawStack; cause-chain capture.

Surface/entitlement: harden.* build hints, HardeningPreflight (fail the build on
local/source targets, invalid level, on-device-debug), Executor.hardenSourceJar/
runBuild wiring, a read-only Hardening status API, and the App-Hardening developer
guide chapter. Also fixes the invalid build_key literal, the BuildHintEditor
grouped-Select values lookup, and the "obfuscates by default" overclaim in the
security chapter.

Tests: 25 unit tests across the two modules and the crash payload (full ProGuard
round-trip, string round-trip + plaintext-absence, control-flow verification,
mapping retrace, trace-format detection, pre-flight truth table).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 6, 2026 10:48

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ae40837827

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java Outdated
Comment thread maven/codenameone-maven-plugin/pom.xml Outdated
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

Generated automatically by the PR CI workflow.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces an Enterprise-gated, cross-platform “App Hardening” pipeline (bytecode-level renaming + string encryption + optional control-flow obfuscation) and integrates it with Crash Protection so hardened builds can still be symbolicated using a retained mapping.

Changes:

  • Adds new Maven modules: cn1-hardening (engine) and cn1-retrace (mapping/trace parsing + retrace CLI).
  • Integrates hardening into the Maven plugin build flow (preflight checks + engine invocation + platform IDs), and updates Crash Protection payload schema to include rawStack/traceFormat/mappingId/hardenLevel.
  • Improves ParparVM Throwable.getStackTrace() by parsing the native stack string into structured frames.

Reviewed changes

Copilot reviewed 54 out of 55 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
vm/JavaAPI/src/java/lang/Throwable.java Parse ParparVM stack text into StackTraceElements
tests/core/test/com/codename1/crash/CrashReportPayloadTest.java Tests for traceFormat + hardening fields JSON
Ports/JavaSE/src/com/codename1/impl/javase/BuildHintSchemaDefaults.java Adds hardening build-hint schema defaults
Ports/JavaSE/src/com/codename1/impl/javase/BuildHintEditor.java Fix grouped Select .values resolution
maven/pom.xml Adds hardening/retrace modules; bumps ProGuard
maven/codenameone-maven-plugin/src/test/java/com/codename1/maven/HardeningPreflightTest.java Unit tests for hardening preflight matrix
maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/HardeningPreflight.java Client-side preflight validation logic
maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/CN1BuildMojo.java Invoke preflight; route local builds via runBuild
maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/WindowsNativeBuilder.java Provides hardening platform id
maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/LinuxNativeBuilder.java Provides hardening platform id
maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/JavaScriptBuilder.java Provides hardening platform id
maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java Provides hardening platform id
maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java Fork/execute hardening engine; mappingId/buildKey handling
maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java Android: disable engine renaming; mappingId + encoded build_key
maven/codenameone-maven-plugin/pom.xml Embeds shaded hardening jar into plugin resources
maven/cn1-retrace/src/test/java/com/codename1/retrace/ParparVmTraceParserTest.java Golden tests for ParparVM trace parser
maven/cn1-retrace/src/test/java/com/codename1/retrace/MappingFileTest.java Tests for mapping parse + retrace + chaining
maven/cn1-retrace/src/main/java/com/codename1/retrace/RetraceMain.java Standalone retrace CLI entrypoint
maven/cn1-retrace/src/main/java/com/codename1/retrace/ParparVmTraceParser.java Parses ParparVM text traces; rejects JS stacks
maven/cn1-retrace/src/main/java/com/codename1/retrace/MappingFile.java Parses/inverts ProGuard mapping for retrace
maven/cn1-retrace/src/main/java/com/codename1/retrace/MappingChain.java Applies multiple mappings in order
maven/cn1-retrace/src/main/java/com/codename1/retrace/Frame.java Frame DTO for retrace pipeline
maven/cn1-retrace/pom.xml Module build + shaded standalone jar
maven/cn1-hardening/src/test/java/com/codename1/hardening/StringEncryptTransformTest.java End-to-end string encryption tests
maven/cn1-hardening/src/test/java/com/codename1/hardening/fixture/Secrets.java Fixture class for encryption tests
maven/cn1-hardening/src/test/java/com/codename1/hardening/fixture/Helper.java Fixture class for rename tests
maven/cn1-hardening/src/test/java/com/codename1/hardening/ControlFlowTransformTest.java Control-flow guard verification tests
maven/cn1-hardening/src/main/java/com/codename1/hardening/StringEncryptTransform.java ASM transform: encrypt LDC + ConstantValue strings
maven/cn1-hardening/src/main/java/com/codename1/hardening/ProGuardRunner.java Programmatic ProGuard rename runner
maven/cn1-hardening/src/main/java/com/codename1/hardening/OutputVerifier.java CheckClassAdapter verification gate
maven/cn1-hardening/src/main/java/com/codename1/hardening/MappingWriter.java Mapping header + mappingId computation
maven/cn1-hardening/src/main/java/com/codename1/hardening/MangleCollisionCheck.java Guards ParparVM symbol mangle collisions
maven/cn1-hardening/src/main/java/com/codename1/hardening/Main.java Engine CLI front-end + exit codes
maven/cn1-hardening/src/main/java/com/codename1/hardening/JarDemuxer.java Split/rebuild fat jar around class entries
maven/cn1-hardening/src/main/java/com/codename1/hardening/InputJarKeepScanner.java ASM scan for reflective keep rules
maven/cn1-hardening/src/main/java/com/codename1/hardening/HardeningResult.java Engine result model
maven/cn1-hardening/src/main/java/com/codename1/hardening/HardeningRequest.java Engine request model
maven/cn1-hardening/src/main/java/com/codename1/hardening/HardeningProfile.java Hardening levels + defaults
maven/cn1-hardening/src/main/java/com/codename1/hardening/HardeningException.java Engine exception type
maven/cn1-hardening/src/main/java/com/codename1/hardening/HardeningEngine.java Pipeline orchestration + safety gates
maven/cn1-hardening/src/main/java/com/codename1/hardening/HardeningConfig.java Parses harden.* hints into config
maven/cn1-hardening/src/main/java/com/codename1/hardening/ControlFlowTransform.java ASM opaque-predicate entry guards
maven/cn1-hardening/src/main/java/com/codename1/hardening/Cn1NameFactory.java zq-prefixed dictionary generator
maven/cn1-hardening/src/main/java/com/codename1/hardening/BuiltinKeepRules.java Tier-1 keep rules + shared R8 rules
maven/cn1-hardening/pom.xml Engine module deps + shaded standalone jar
docs/developer-guide/security.asciidoc Correct obfuscation claims; reference hardening
docs/developer-guide/developer-guide.asciidoc Include App-Hardening chapter
docs/developer-guide/Crash-Protection.asciidoc Document new crash payload fields + hardening flow
docs/developer-guide/App-Hardening.asciidoc New hardening chapter
CodenameOne/src/com/codename1/security/hardening/package-info.java Package docs for Hardening API
CodenameOne/src/com/codename1/security/hardening/Hardening.java Read-only hardening status API
CodenameOne/src/com/codename1/crash/PiiScrubber.java Add scrubRawStack hook
CodenameOne/src/com/codename1/crash/CrashReportPayload.java Add rawStack/traceFormat/mappingId/hardenLevel
CodenameOne/src/com/codename1/crash/CrashProtection.java Capture/scrub rawStack; include in payload
Suppressed comments (2)

maven/cn1-hardening/src/main/java/com/codename1/hardening/HardeningConfig.java:99

  • Any unrecognized harden.strings value currently falls into the final else branch and enables maximum string encryption. This can turn on expensive/behaviour-changing encryption accidentally (e.g. a typo), so the parser should only treat explicit "all"/"true"/"2"/"3" as 'all' and otherwise fall back to the level defaults (or fail).
            } else {
                // "all", "true", "2", "3"
                encConst = true;
                encAll = true;
            }

docs/developer-guide/Crash-Protection.asciidoc:93

  • This paragraph states that on ParparVM ports the Java trace arrives only as rawStack and is parsed server-side. With Throwable.getStackTrace() now parsing the ParparVM stack string into structured frames on-device, the server may receive both; consider updating this sentence to reflect that rawStack is still parparvm-text but frames may be structured too (and rawStack may primarily be for the cause-chain / verbatim trace).
When a build is hardened (see <<app-hardening>>), the build server retains the cross-platform obfuscation mapping and symbolicates incoming reports against it, so a hardened build's crashes still land as readable, correctly-lined issues. Two things follow from how the mapping is retained: a report whose mapping has aged out of retention can no longer be retraced, and a locally hardened build -- whose mapping never reached the server -- can't be symbolicated at all. On the ParparVM ports (iOS, tvOS, watchOS, mac-native, Windows, Linux) the Java trace arrives as `rawStack` in the `parparvm-text` format and is parsed server-side; on the JavaScript port it arrives as a JavaScript engine stack (`js-error`) and is symbolicated best-effort through the source map.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread docs/developer-guide/Crash-Protection.asciidoc Outdated
Comment thread CodenameOne/src/com/codename1/crash/CrashReportPayload.java Outdated
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

✅ ByteCodeTranslator Quality Report

Test & Coverage

  • Tests: 420 total, 0 failed, 14 skipped

Benchmark Results

  • Execution Time: 22711 ms

  • Hotspots (Top 20 sampled methods):

    • 17.79% java.util.ArrayList.indexOf (355 samples)
    • 9.52% com.codename1.tools.translator.BytecodeMethod.addToConstantPool (190 samples)
    • 4.51% com.codename1.tools.translator.BytecodeMethod.equals (90 samples)
    • 3.31% java.lang.StringBuilder.append (66 samples)
    • 3.06% com.codename1.tools.translator.ByteCodeClass.hasDeclaredMethod (61 samples)
    • 2.71% com.codename1.tools.translator.Parser.cn1EnsureSubclassIndex (54 samples)
    • 2.66% com.codename1.tools.translator.ByteCodeClass.findDeclaredMethod (53 samples)
    • 2.56% com.codename1.tools.translator.BytecodeMethod.optimize (51 samples)
    • 2.40% com.codename1.tools.translator.bytecodes.Invoke.findMethodUp (48 samples)
    • 2.10% org.objectweb.asm.tree.analysis.Analyzer.analyze (42 samples)
    • 1.75% com.codename1.tools.translator.Parser.classIndex (35 samples)
    • 1.65% com.codename1.tools.translator.bytecodes.Invoke.resolveDirectTarget (33 samples)
    • 1.55% java.lang.String.equals (31 samples)
    • 1.50% org.objectweb.asm.tree.analysis.Analyzer.findSubroutine (30 samples)
    • 1.40% java.lang.Object.hashCode (28 samples)
    • 1.35% java.util.HashMap.hash (27 samples)
    • 1.15% sun.nio.fs.UnixNativeDispatcher.open0 (23 samples)
    • 1.10% com.codename1.tools.translator.BytecodeMethod.appendCMethodPrefix (22 samples)
    • 1.10% com.codename1.tools.translator.Parser.generateClassAndMethodIndexHeader (22 samples)
    • 1.05% com.codename1.tools.translator.ByteCodeClass.markDependent (21 samples)
  • ⚠️ Coverage report not generated.

Static Analysis

  • ✅ SpotBugs: no findings (report was not generated by the build).
  • ⚠️ PMD report not generated.
  • ⚠️ Checkstyle report not generated.

Generated automatically by the PR CI workflow.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Cloudflare Preview

- Complete GPLv2+Classpath header on the 4 files the copyright gate flagged
  (BuildHintEditor had none; BuildHintSchemaDefaults + the two new tests were short).
- Convert Hardening.java/package-info.java to /// markdown comments (core src gate).
- Declare cn1-hardening:standalone as a runtime-scope plugin dependency so the reactor
  builds the engine before the plugin embeds it (fixes the antrun copy failing in CI);
  fix an illegal -- inside the new XML comment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 6, 2026 11:15
ProGuard 7.3.2 cannot read class files newer than JDK 20 (it fails on the JDK's
own module classes), so the renamer must run on JDK 8-20 -- the cloud daemon
forks the engine on JDK 17. The engine now fails with a clear message instead of
a cryptic ProGuard error when renaming is requested on a too-new JVM, and the
ProGuard-dependent tests skip (JUnit assumption) on JDK 21+ so the PR CI JDK-21
leg stays green. String encryption and control-flow tests have no such limit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fc8822afde

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java Outdated
Comment thread maven/cn1-hardening/src/main/java/com/codename1/hardening/BuiltinKeepRules.java Outdated
- FrameClassWriter: COMPUTE_FRAMES resolved common superclasses through the
  engine's own classloader, which lacks the app/library classes when run as a
  forked jar, so any class with a merge between application types aborted
  hardening. Resolve the hierarchy from a classloader over the (renamed) input
  classes plus the library jars, falling back to Object. Threaded through the
  string-encryption and control-flow transforms; unit-tested. (Codex P1)
- Pass the FULLY QUALIFIED main class to the keep rules: getMainClass() is the
  simple name, so a bare value kept a default-package class and let ProGuard
  rename the real application class out from under the generated stub. Fixed in
  both the plugin and daemon config writers. (Codex P1)
- The reactor dependency forcing cn1-hardening to build before the plugin (so
  the engine jar exists for the embed step) already landed in the prior commit. (Codex P1)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 56 out of 57 changed files in this pull request and generated no new comments.

Suppressed comments (3)

Ports/JavaSE/src/com/codename1/impl/javase/BuildHintEditor.java:206

  • Select-hint value parsing uses the last character of the values string as the delimiter (charAt(len-1)), which breaks all of the comma-separated .values entries coming from BuildHintSchemaDefaults (e.g. off,standard,aggressive,paranoid will split on d). This prevents the new Hardening Select hints (and existing ones like nativeTheme) from populating correctly.
                            String separator = ""+valuesString.charAt(valuesString.length()-1);
                            ArrayList<String> values = new ArrayList<String>();
                            values.add("");
                            for (String value : valuesString.split(separator)) {

docs/developer-guide/Crash-Protection.asciidoc:86

  • This section says ParparVM ports rely on rawStack because getStackTrace() yields only a formatted string, but this PR updates vm/JavaAPI/java/lang/Throwable.getStackTrace() to parse the ParparVM text stack into structured StackTraceElement[]. The docs should be updated to reflect that structured frames are now available on ParparVM (and rawStack is supplemental / for the full rendered text and cause chain).
- `frames[]` -- class / method / file / line / `native` flag per frame
- `rawStack` -- the pre-rendered Java stack (via `printStackTrace`, including the cause chain). On the ParparVM ports this is the readable Java trace, since `getStackTrace()` there yields a formatted string rather than structured frames
- `traceFormat` -- how the server should read `rawStack`: `structured`, `parparvm-text`, `js-error`, or `none`. Derived, never guessed

maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java:2457

  • The generic catch (Exception e) wraps the error without the original cause, which makes diagnosing hardening failures much harder (stack trace is lost). Preserve the cause in the BuildException.

Copilot AI review requested due to automatic review settings August 6, 2026 11:28

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: efa163b074

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread maven/cn1-hardening/src/main/java/com/codename1/hardening/OutputVerifier.java Outdated
Comment thread Ports/JavaSE/src/com/codename1/impl/javase/BuildHintSchemaDefaults.java Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 56 out of 57 changed files in this pull request and generated no new comments.

Suppressed comments (2)

vm/JavaAPI/src/java/lang/Throwable.java:144

  • setStackTrace() updates parsedStack, but printStackTrace() prints the pre-rendered "stack" string field. Because setStackTrace() doesn't update "stack", callers that set a custom stack trace will still see the old/empty stack printed (and CrashProtection.safeRawStack() relies on printStackTrace()). Rebuild the "stack" string from the provided StackTraceElement[] so printStackTrace() reflects the updated trace.
    CodenameOne/src/com/codename1/crash/CrashReportPayload.java:120
  • CrashReportPayload documents hardenLevel as one of "off"/"standard"/"aggressive"/"paranoid", but it currently defaults to the empty string when the property isn't set. This makes the payload ambiguous for unhardened builds and diverges from Hardening.getLevel() which defaults to "off".
        this.osVersion = d.getProperty("OSVer", "");
        this.mappingId = d.getProperty("cn1.mappingId", "");
        this.hardenLevel = d.getProperty("cn1.hardenLevel", "");
        Locale loc = Locale.getDefault();

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

Codex P1/P2:
- harden.keep: split on newlines only (a ';' is legal inside a rule body).
- Keep SourceFile,LineNumberTable so ParparVM/native traces keep real line
  numbers for retrace.
- Honor constants-vs-all string mode: 'constants' encrypts only values declared
  as static-final String constants (and javac's inlined copies), 'all' encrypts
  every literal.
- Propagate cn1.mappingId/cn1.hardened/cn1.hardenLevel into the request before
  stub generation; Android stub now stamps them (Hardening.isHardened(), crash
  report mappingId/level).
- Supply the compile/platform classpath to ProGuard as library jars so an app
  method overriding a framework method is not renamed apart from its superclass.
- Append harden.keep + the name-bound PropertyBusinessObject keep to Android's
  R8 config (Android keeps R8 as sole renamer).

Build fixes:
- CrashProtection.safeRawStack: build the raw stack with StringBuilder instead of
  java.io.PrintWriter, which the core's CLDC11 bootclasspath (ANT build) lacks.
- Embed the engine jar via maven-dependency-plugin:copy (resolves the standalone
  artifact from the reactor/repo) so partial plugin-only CI builds no longer fail
  copying from an unbuilt sibling target/.
- Keep the test resource bytes ASCII (explicit byte[] rather than a non-ASCII
  literal).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 6, 2026 11:52

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 605f722c41

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/crash/CrashProtection.java Outdated
…, Select delimiters)

- OutputVerifier: pass the input/library hierarchy classloader to
  CheckClassAdapter.verify so the final verification pass resolves application
  types instead of loading them from the engine's classpath (a class with a merge
  between app types would otherwise fail verification). (P1)
- Keep every class named by a META-INF/services/* descriptor (the service
  interface and each provider), since the descriptors are copied verbatim and
  ServiceLoader would break if they were renamed; regression-tested. (P1)
- Terminate the hardening Select .values lists with their delimiter, which
  BuildHintEditor reads as the last character, so the simulator shows the real
  options instead of splitting on a letter. (P2)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@shai-almog

shai-almog commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 12 screenshots: 12 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0fffb4c59f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread maven/cn1-hardening/src/main/java/com/codename1/hardening/Main.java Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 57 out of 57 changed files in this pull request and generated no new comments.

Suppressed comments (5)

maven/cn1-hardening/src/main/java/com/codename1/hardening/JarDemuxer.java:120

  • rebuild() doesn’t close its ZipOutputStream. Even with an explicit finish(), closing the stream is the reliable way to release the deflater/native resources and ensure the central directory is flushed.
        FileOutputStream fo = new FileOutputStream(outJar);
        try {
            ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(fo));
            for (Map.Entry<String, byte[]> e : classesByInternalName.entrySet()) {
                ZipEntry entry = new ZipEntry(e.getKey() + ".class");

maven/cn1-hardening/src/main/java/com/codename1/hardening/JarDemuxer.java:145

  • readClasses() creates a ZipInputStream but never closes it. Closing only the underlying FileInputStream works most of the time, but it’s safer and clearer to close the ZIP stream explicitly (try-with-resources).
        FileInputStream fi = new FileInputStream(jar);
        try {
            ZipInputStream zis = new ZipInputStream(fi);
            ZipEntry entry;
            while ((entry = zis.getNextEntry()) != null) {

Ports/JavaSE/src/com/codename1/impl/javase/BuildHintEditor.java:201

  • propName.indexOf("}}.") is assumed to be present here. If it isn’t (unexpected/third-party schema key), the current substring(... + 3) call will produce an incorrect key (and can throw if the key is shorter), and valuesString.charAt(valuesString.length()-1) will also throw on an empty values string. Consider guarding the index/length and making the value-splitting robust (comma-separated lists in this repo don’t end with a delimiter).
                        String valuesKey = propName.substring(0, propName.indexOf("}}.")+3) + "values";
                        String valuesString = System.getProperty(valuesKey);
                        if (valuesString == null) {
                            valuesString = System.getProperty("codename1.arg.{{ "+model.name+" }}.values");
                        }

CodenameOne/src/com/codename1/crash/CrashReportPayload.java:147

  • deriveTraceFormat() can classify a raw stack as parparvm-text even if the frame body contains spaces. That diverges from the on-device ParparVM parser logic (which rejects frames containing spaces) and could send the server down the wrong parse path for some JS engine stack formats.
        int at = rawStack.indexOf("    at ");
        if (at >= 0) {
            int lineEnd = rawStack.indexOf('\n', at);
            String body = lineEnd < 0 ? rawStack.substring(at + 7) : rawStack.substring(at + 7, lineEnd);
            if (body.indexOf('(') < 0 && body.indexOf('/') < 0 && body.indexOf('@') < 0) {

maven/cn1-hardening/src/main/java/com/codename1/hardening/JarDemuxer.java:103

  • split() creates ZipInputStream/ZipOutputStream but never closes them. Relying on only closing the underlying FileInputStream/FileOutputStream can leak native resources and (for the output) risks incomplete ZIP finalization on some JVMs/filesystems. Use try-with-resources to ensure ZIP streams are closed.

This issue also appears in the following locations of the same file:

  • line 116
  • line 141
            ZipInputStream zis = new ZipInputStream(fi);
            FileOutputStream fo = new FileOutputStream(classesJarOut);
            try {
                ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(fo));
                ZipEntry entry;

Copilot AI review requested due to automatic review settings August 6, 2026 12:05

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 889912753c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread maven/cn1-retrace/src/main/java/com/codename1/retrace/MappingFile.java Outdated
@shai-almog

shai-almog commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 144 screenshots: 144 matched.
✅ Native Apple TV (tvOS, Metal) screenshot tests passed.

Codex:
- Preserve the JavaScript engine stack in rawStack: capture the platform's own
  printStackTrace(PrintStream) rendering instead of rebuilding from structured
  frames (which are empty on the JS port). Added printStackTrace(PrintStream) to
  the CLDC11 API and the ParparVM runtime Throwable; PrintStream (unlike
  PrintWriter) is in the restricted core API.
- Stamp hardening metadata in the iOS stub too (shared Executor helper); Android
  already did. (ParparVM JS/native ports have no runtime-property stub yet -- same
  gap as build_key there.)
- Derive the engine platform for Mac-native builds (harden.mac.enabled now applies).
- Don't stamp the empty engine mapping's constant id on Android (R8 owns the
  real mapping); leave cn1.mappingId empty when the engine doesn't rename.
- Fail an Android build that requests hardening renaming while
  android.enableProguard=false disables R8.

Copilot:
- Control-flow guard uses the 2-arg System.getProperty so it can't NPE when
  java.home is absent (Android).
- The engine (Main) fails loudly on an invalid harden.level instead of treating
  it as off, and gates entitlement only when hardening is actually active so a
  per-platform opt-out works on a non-entitled account.
- Clarify the docs/comment: getStackTrace() now returns structured frames on
  every port; rawStack complements them (and is the JS port's readable trace).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d2ebbef816

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

shai-almog and others added 2 commits August 6, 2026 19:56
- JarDemuxer drops jar signature blocks (META-INF/*.SF|*.RSA|*.DSA|*.EC) when
  rebuilding, since renaming invalidates them and a verifying JarFile would throw
  SecurityException: Invalid signature file digest.
- MappingFile maps distinct obfuscated->original line ranges (R8 / optimized
  ProGuard, e.g. 1:2:...:40:41) back to the source line instead of passing the
  device line through; tested.
- HardeningPreflight honors per-platform opt-outs: a target with
  harden.<platform>.enabled=false is treated as off rather than rejected.
- paranoid is now a genuinely stronger tier: control-flow inserts two nested
  opaque-predicate guards per method (intensity 2) vs one for aggressive; tested.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- String encryption now processes Java 8 interface default/static method bodies
  (previously the whole interface was skipped, leaving their literals plaintext in
  all/paranoid mode). The synthesized decoder is public in an interface (private
  statics are 9+); interface constant fields are still left alone. Tested.
- The engine no longer marks a build hardened when a non-off level has all its
  transforms individually disabled (harden.rename=false + harden.strings=off + no
  control flow): it returns SKIPPED_NOT_REQUESTED instead of rebuilding an
  unchanged jar and stamping cn1.hardened=true. Tested.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fdc77f21a8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread maven/codenameone-maven-plugin/pom.xml Outdated
Comment thread maven/cn1-hardening/src/main/java/com/codename1/hardening/Main.java Outdated
@shai-almog

shai-almog commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 148 screenshots: 148 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 320 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 87ms / native 3ms = 29.0x speedup
SIMD float-mul (64K x300) java 79ms / native 3ms = 26.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 238.000 ms
Base64 CN1 decode 140.000 ms
Base64 native encode 795.000 ms
Base64 encode ratio (CN1/native) 0.299x (70.1% faster)
Base64 native decode 278.000 ms
Base64 decode ratio (CN1/native) 0.504x (49.6% faster)
Base64 SIMD encode 60.000 ms
Base64 encode ratio (SIMD/CN1) 0.252x (74.8% faster)
Base64 SIMD decode 69.000 ms
Base64 decode ratio (SIMD/CN1) 0.493x (50.7% faster)
Base64 encode ratio (SIMD/native) 0.075x (92.5% faster)
Base64 decode ratio (SIMD/native) 0.248x (75.2% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 8.000 ms
Image createMask (SIMD on) 3.000 ms
Image createMask ratio (SIMD on/off) 0.375x (62.5% faster)
Image applyMask (SIMD off) 70.000 ms
Image applyMask (SIMD on) 73.000 ms
Image applyMask ratio (SIMD on/off) 1.043x (4.3% slower)
Image modifyAlpha (SIMD off) 124.000 ms
Image modifyAlpha (SIMD on) 77.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.621x (37.9% faster)
Image modifyAlpha removeColor (SIMD off) 79.000 ms
Image modifyAlpha removeColor (SIMD on) 85.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.076x (7.6% slower)

- Interface decoder invocations use itf=true so ASM writes an InterfaceMethodref,
  not a Methodref -- otherwise an encrypted default/static interface method throws
  IncompatibleClassChangeError at run time.
- Gate entitlement in the engine CLI on willApplyAnyTransform(cfg) rather than
  isActive(), so a level whose transforms are all disabled is skipped (not rejected
  as not-entitled), matching the SKIPPED path.
- Embed the engine jar at the plugin's prepare-package phase, not generate-resources,
  so pr.yml's '-pl codenameone-maven-plugin -am ... test' (which only advances the
  upstream module through test, before its package/shade) no longer fails resolving
  the standalone classifier; package/install still embed it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@shai-almog

shai-almog commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 217 screenshots: 217 matched.
✅ Native Apple Watch (watchOS, Core Graphics) screenshot tests passed.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6efa3efaf0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread maven/cn1-hardening/src/main/java/com/codename1/hardening/HardeningConfig.java Outdated
- Track rename *requested* (intent) vs rename *enabled* (engine does it). On Android
  renameEnabled is false but R8 performs the requested rename, so willApplyAnyTransform
  now counts a rename-only Android build as hardened (transform 'rename:r8') instead of
  skipping it and leaving cn1.hardened=false. Tested.
- The Android R8/enableProguard conflict check respects harden.and.enabled=false: an
  explicitly opted-out Android target no longer fails that check.
- Reject an unknown harden.strings value (e.g. a typo 'constant') in the engine CLI
  instead of silently enabling the most invasive 'all' mode; the config also falls back
  to the level default rather than 'all' for an unrecognized value.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d354db3eed

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread maven/codenameone-maven-plugin/pom.xml Outdated
@shai-almog

shai-almog commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 542 seconds

Build and Run Timing

Metric Duration
Simulator Boot 89000 ms
Simulator Boot (Run) 1000 ms
App Install 25000 ms
App Launch 2000 ms
Test Execution 574000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 73ms / native 4ms = 18.2x speedup
SIMD float-mul (64K x300) java 65ms / native 3ms = 21.6x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 592.000 ms
Base64 CN1 decode 361.000 ms
Base64 native encode 1964.000 ms
Base64 encode ratio (CN1/native) 0.301x (69.9% faster)
Base64 native decode 679.000 ms
Base64 decode ratio (CN1/native) 0.532x (46.8% faster)
Base64 SIMD encode 63.000 ms
Base64 encode ratio (SIMD/CN1) 0.106x (89.4% faster)
Base64 SIMD decode 127.000 ms
Base64 decode ratio (SIMD/CN1) 0.352x (64.8% faster)
Base64 encode ratio (SIMD/native) 0.032x (96.8% faster)
Base64 decode ratio (SIMD/native) 0.187x (81.3% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 32.000 ms
Image createMask (SIMD on) 4.000 ms
Image createMask ratio (SIMD on/off) 0.125x (87.5% faster)
Image applyMask (SIMD off) 202.000 ms
Image applyMask (SIMD on) 97.000 ms
Image applyMask ratio (SIMD on/off) 0.480x (52.0% faster)
Image modifyAlpha (SIMD off) 96.000 ms
Image modifyAlpha (SIMD on) 269.000 ms
Image modifyAlpha ratio (SIMD on/off) 2.802x (180.2% slower)
Image modifyAlpha removeColor (SIMD off) 131.000 ms
Image modifyAlpha removeColor (SIMD on) 310.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 2.366x (136.6% slower)

- The synthesized decoder returns an interned String, so reference (==) equality
  that Java guarantees for string literals/constants still holds after encryption
  (two decodes of the same literal, and a constant vs its inlined readers, are now
  the same object). Tested.
- MappingFile retains the original range's end bound: a single-line original range
  (e.g. 1:3:...:40:40) collapses every covered line to that line, and a shorter
  original range is clamped instead of overshooting. Tested.
- The plugin depends on the UNCLASSIFIED cn1-hardening artifact for reactor ordering
  (resolvable from target/classes during '-am ... test'), provided+optional with a
  wildcard exclusion so ProGuard/ASM stay off the plugin classpath; the shaded
  'standalone' jar is still pulled by the dependency-plugin copy at package.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 71b9c6830c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread maven/cn1-hardening/src/main/java/com/codename1/hardening/Main.java
Comment thread maven/cn1-hardening/src/main/java/com/codename1/hardening/HardeningEngine.java Outdated
The round-9 dependency comment contained a literal '--', which strict Maven XML
parsers reject (ProjectBuildingException), cascading to every job that reads the
plugin POM. Local Maven was lenient; CI is not.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@shai-almog

shai-almog commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 143 screenshots: 143 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 480 seconds

Build and Run Timing

Metric Duration
Simulator Boot 108000 ms
Simulator Boot (Run) 1000 ms
App Install 18000 ms
App Launch 57000 ms
Test Execution 538000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 66ms / native 3ms = 22.0x speedup
SIMD float-mul (64K x300) java 155ms / native 8ms = 19.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 454.000 ms
Base64 CN1 decode 96.000 ms
Base64 native encode 642.000 ms
Base64 encode ratio (CN1/native) 0.707x (29.3% faster)
Base64 native decode 866.000 ms
Base64 decode ratio (CN1/native) 0.111x (88.9% faster)
Base64 SIMD encode 50.000 ms
Base64 encode ratio (SIMD/CN1) 0.110x (89.0% faster)
Base64 SIMD decode 46.000 ms
Base64 decode ratio (SIMD/CN1) 0.479x (52.1% faster)
Base64 encode ratio (SIMD/native) 0.078x (92.2% faster)
Base64 decode ratio (SIMD/native) 0.053x (94.7% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 22.000 ms
Image createMask (SIMD on) 35.000 ms
Image createMask ratio (SIMD on/off) 1.591x (59.1% slower)
Image applyMask (SIMD off) 57.000 ms
Image applyMask (SIMD on) 228.000 ms
Image applyMask ratio (SIMD on/off) 4.000x (300.0% slower)
Image modifyAlpha (SIMD off) 298.000 ms
Image modifyAlpha (SIMD on) 118.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.396x (60.4% faster)
Image modifyAlpha removeColor (SIMD off) 137.000 ms
Image modifyAlpha removeColor (SIMD on) 206.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.504x (50.4% slower)

- willApplyAnyTransform returns false when the platform is opted out
  (harden.<platform>.enabled=false), so a non-entitled build of an opted-out target
  is skipped rather than rejected as not-entitled. Tested.
- Seed the rename dictionary: Cn1NameFactory.writeDictionary shifts the starting word
  by the seed / build key, so harden.seed actually changes the mapping (and the same
  seed reproduces it) instead of every build getting identical names. Tested.
- Make ParparVM String.intern() atomic (synchronized on the shared pool), so concurrent
  interning of equal decoded literals returns the same object and can't corrupt the
  pool -- the port-specific root cause behind the decoder's canonical-string guarantee.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 495a599908

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/developer-guide/App-Hardening.asciidoc Outdated
- Control-flow guard derives its predicate from Runtime.getRuntime().
  availableProcessors() (contractually >= 1, unfoldable) instead of a system
  property whose value could be present-but-empty and collapse the guard into its
  dead arm.
- IPhoneBuilder reports the 'mac' hardening platform whenever macNative.enabled=true
  (the signal the native-Mac target actually sets), so harden.mac.enabled applies to
  the Mac output; the previous ios.enabled check was never set by any producer.
- Docs: harden.keep is one-rule-per-line (newline-separated only); stop documenting
  ';'-separation, which the parser can't use because ';' is legal inside a rule body.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c94e6dd9b8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread maven/cn1-hardening/src/main/java/com/codename1/hardening/HardeningEngine.java Outdated
- InputJarKeepScanner now also collects static-final String field ConstantValue
  attributes, so a class named for reflection only in a constant field (never an LDC)
  is kept instead of renamed.
- The engine exports its derived keep rules to a --r8keep file; on Android (where R8 is
  the sole renamer and the engine does not rename) Executor passes the file and
  AndroidGradleBuilder feeds it to proguard.cfg, so reflectively referenced classes reach
  R8 rather than being renamed out from under the lookup.
- MangleCollisionCheck runs only for the ParparVM-C targets (ios/mac/watch/tv/win/linux)
  whose symbol mangle can actually alias two names; on Android/JavaSE a.b_c and a.b.c stay
  distinct, so the check no longer aborts legal builds.
- The native-Mac targets resolve their harden.<platform>.enabled opt-out from the build
  target (mac), matching IPhoneBuilder, instead of the platform=ios they run under.
- Two regression tests: scanner keeps a class named only by a field constant; Android run
  exports reflection + main + harden.keep rules to the R8 keep file.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b625567eec

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

- Stamp cn1.mappingId / cn1.hardened / cn1.hardenLevel in the JavaScript, Linux and
  Windows launchers via the shared hardeningRuntimeProperties helper, so
  Hardening.isHardened() and crash payloads carry the mapping id / level on those ports
  too (parity with iOS and Android). On JavaScript the stamp runs right after
  ParparVMBootstrap.bootstrap returns, when Display is live.
- Parse the Android harden.rename / harden.and.enabled opt-outs with a shared tri-state
  helper (Executor.hardenBoolArg) matching HardeningConfig.boolTri, so harden.rename=off
  and =0 behave like =false instead of being misread as 'renaming still requested' and
  rejecting the build with R8 disabled. Regression test HardeningBooleanArgTest.
- Add 'symbolicates' to languagetool-accept.txt (fixes the red developer-guide quality
  gate: 2 MORFOLOGIK matches on the Crash-reports paragraph).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: df007c4400

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@@ -417,6 +422,12 @@ private File writeLauncher(File workDir, String launcherName, String packageName
}
}
pw.println(" ParparVMBootstrap.bootstrap(new " + mainClass + "());");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Stamp JavaScript metadata before lifecycle callbacks

In hardened JavaScript builds, this call runs the application's lifecycle before the following metadata stamp: ParparVMBootstrap.bootstrap() calls bootstrap.run(), which invokes both lifecycle.init() and lifecycle.start(). Therefore Hardening.isHardened() returns false during normal initialization, and a crash from either callback is uploaded without the mapping ID or level. Fresh evidence beyond the earlier launcher-metadata fix is the synchronous lifecycle execution inside Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/ParparVMBootstrap.java; arrange for the properties to be set after Display.init() but before run().

Useful? React with 👍 / 👎.

cfg.getPlatform(), req.getBuildKey());
}

HardeningResult result = HardeningResult.hardened(req.getOutputJar(), mappingFile);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip builds when no requested transform changes anything

When the effective configuration predicts a transform but the input contains no eligible targets—for example, harden.level=standard, harden.rename=false, and no static-final string longer than two characters—encryptedStrings and guardedMethods remain zero and transformsApplied is empty, yet this still returns HARDENED after adding only the marker. The executor consequently stamps cn1.hardened=true for an otherwise unchanged application. Fresh evidence beyond the earlier empty-configuration fix is this data-dependent no-op path; return a skipped result when the completed transform set is empty.

Useful? React with 👍 / 👎.

|Class/method/field renaming |-- |yes |yes |yes
|String encryption |-- |constants |all |all + reflective names
|Control-flow obfuscation |-- |-- |yes |yes + opaque predicates
|Debug / line-number stripping |-- |yes |yes |yes

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not claim hardening strips line-number metadata

This table tells users that every enabled level strips debug and line-number data, but the generated ProGuard flags explicitly preserve SourceFile and LineNumberTable, and Android's generated R8 configuration preserves the same attributes so crash lines remain available. A developer choosing a hardening tier on the premise that this metadata is removed therefore receives a materially different security posture; either implement the advertised stripping without breaking retracing or document that these attributes remain.

Useful? React with 👍 / 👎.

The round-13 hardening-metadata stamp emits Display.getInstance().setProperty(...) into
the generated JavaScript launcher, but the launcher had no import for com.codename1.ui.Display
(unlike the Linux/Windows bootstrap stubs, which already import it), breaking the initializr
JavaScript build in the Build website CI step. Add the import.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9fa7384837

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// Propagate the mapping id / hardened flag / level into the request BEFORE the
// builder generates its stubs, so the stubs stamp them as runtime properties
// (Hardening.isHardened(), the crash report's mappingId/hardenLevel).
request.putArgument("cn1.mappingId", lastHardeningMappingId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stamp the Android R8 mapping identifier

For Android rename-enabled builds, the engine intentionally leaves lastHardeningMappingId empty because the actual mapping is produced later by R8, but this assignment then stamps that empty value into the request and AndroidGradleBuilder embeds it in CN1_MAPPING_ID. Consequently hardened Android crash reports cannot identify the exact R8 mapping used for retracing; the R8 mapping's identifier needs to be computed and propagated instead of overwriting the property with the empty engine mapping ID.

Useful? React with 👍 / 👎.

Comment on lines +170 to +172
if (ldc.cst instanceof String && shouldEncryptLiteral((String) ldc.cst)) {
String plain = (String) ldc.cst;
ldc.cst = encode(plain, base);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid oversized encrypted LDC constants

When an eligible literal is large but valid in the original class file, this replacement can make the transformed class unwritable: for example, a 30,000-character ASCII literal fits below the constant-pool modified-UTF-8 limit, while the XOR result consists mostly of three-byte characters and exceeds the 65,535-byte limit. ASM then throws IllegalArgumentException: UTF8 string too large during cn.accept(cw), so aggressive/paranoid hardening fails for otherwise valid applications; split or otherwise encode large payloads without placing the expanded ciphertext in one UTF-8 constant.

Useful? React with 👍 / 👎.

Comment on lines +146 to +149
// Channel 2: static final String ConstantValue attributes (both modes). Skipped on
// interfaces, whose fields are implicitly constant and have no rewritable init slot.
if (!isInterface) {
changed |= encryptStaticFinalStrings(cn, base);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Encrypt interface ConstantValue fields

When an interface declares a string constant such as String TOKEN = "secret", this branch leaves its ConstantValue attribute untouched, so standard, aggressive, and paranoid builds still contain that plaintext despite claiming to handle the field-constant channel. Java 8 interfaces can have a <clinit> for non-constant field initialization, so these values can be moved to a decoder call just as class fields are rather than skipping every interface field.

Useful? React with 👍 / 👎.

@shai-almog

shai-almog commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 146 screenshots: 146 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 62ms / native 6ms = 10.3x speedup
SIMD float-mul (64K x300) java 62ms / native 4ms = 15.5x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 198.000 ms
Base64 CN1 decode 141.000 ms
Base64 SIMD encode 99.000 ms
Base64 encode ratio (SIMD/CN1) 0.500x (50.0% faster)
Base64 SIMD decode 98.000 ms
Base64 decode ratio (SIMD/CN1) 0.695x (30.5% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 30.000 ms
Image createMask (SIMD on) 107.000 ms
Image createMask ratio (SIMD on/off) 3.567x (256.7% slower)
Image applyMask (SIMD off) 65.000 ms
Image applyMask (SIMD on) 60.000 ms
Image applyMask ratio (SIMD on/off) 0.923x (7.7% faster)
Image modifyAlpha (SIMD off) 64.000 ms
Image modifyAlpha (SIMD on) 57.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.891x (10.9% faster)
Image modifyAlpha removeColor (SIMD off) 42.000 ms
Image modifyAlpha removeColor (SIMD on) 33.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.786x (21.4% faster)

@shai-almog

shai-almog commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 181 screenshots: 181 matched.
✅ JavaScript-port screenshot tests passed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants