Skip to content

About

The bypass logic project is an open source project to remove code from JVM based languages using an annotation and gradle flag.

Resources

Stars

1 star

Watchers

2 watching

Forks

Repository files navigation

image

License Gradle ASM 9

One codebase, many product tiers.
Strip premium logic out of your compiled bytecode with a single annotation and a Gradle flag.

BypassLogic

BypassLogic is a Gradle plugin that removes the body of annotated methods at build time and replaces it with a safe default return. The code isn't hidden behind a runtime if (isPremium) check. It simply doesn't exist in the artifact you ship.

Why?

When you deliver software to run on the customer's premises, you hand over your binaries. Feature gating done at runtime (license checks, feature flags, config switches) has two weaknesses:

  • Reverse engineering: a decompiler shows exactly what the paid feature does.
  • Accidental or deliberate unlocking: flipping one flag, patching one boolean, or editing one config value can enable features the customer didn't pay for.

BypassLogic removes the problem at the source. Annotate the premium methods, build with the flag on, and the shipped .class files contain nothing but a default return.

Example

Source code:

@BypassLogic
public void processPaidPlanRequest(String userId) {
    if (!isValidUser(userId)) {
        return;
    }

    handlePaidPlanUserRequest();
}

private boolean isValidUser(String userId) {
    return ...;
}

Decompiled .class after a build with BypassLogic enabled:

public void processPaidPlanRequest(String userId) {
    return;
}

Methods with a return value get a sensible default (see Default return values):

@BypassLogic
public List<Report> premiumReports(String tenantId) {
    // expensive proprietary logic
}

becomes

public List<Report> premiumReports(String tenantId) {
    return Collections.emptyList(); // empty collection
}

How it works

diagram
  1. The plugin hooks into every JavaCompile task and runs after compilation (doLast).
  2. Each generated .class file is scanned cheaply (SKIP_CODE, SKIP_DEBUG, SKIP_FRAMES) for the @BypassLogic annotation. Classes without it are left untouched.
  3. Classes that contain the annotation are rewritten with ASM. A MethodVisitor replaces the body of each annotated method with an instruction sequence that returns the default value for the method's return type.
  4. Stack map frames are recomputed (COMPUTE_FRAMES), with a custom ClassWriter that resolves common super classes through the project's compile classpath, so the output passes JVM bytecode verification.

Because the transformation works on bytecode, there's no annotation processor, no source rewriting, and no change to your IDE workflow.

Quick start

1. Apply the plugin and add the annotation

// build.gradle
plugins {
    id 'java'
    id '<plugin-id>' version '<version>'   // TODO: fill in once published
}

dependencies {
    compileOnly 'assaviv:bypass-logic-annotations:<version>'   // TODO: fill in coordinates
}

2. Annotate what should be removed

import assaviv.annotations.BypassLogic;

public class ReportService {

    @BypassLogic
    public List<Report> premiumReports(String tenantId) {
        ...
    }
}

3. Turn stripping on for the build that ships to the customer

bypassLogicConfig {
    enabled = project.hasProperty('community')
}
./gradlew build                 # full build, all features present
./gradlew build -Pcommunity     # stripped build, annotated logic removed

enabled = false (the default behavior of a normal build) leaves your bytecode completely untouched.

Default return values

Return type Replacement
void immediate return
int, byte, short, char 0
long, float, double 0
boolean false
String default String value
Integer, Byte, Short, Character, Long, Float, Double, Boolean default boxed value
List, Set, Map empty collection
Optional Optional.empty()
arrays empty array
any other object null

Design your callers so these defaults mean "feature not available" (empty result, false, null), and handle them gracefully.

Limitations and threat model

BypassLogic is a code-removal tool, not a complete protection scheme. Please keep these points in mind:

  • It removes method bodies only. Class names, method signatures, field names, and string constants outside the removed bodies remain visible. An attacker can still see that a feature exists, just not how it works.
  • Callers are not changed. Code that calls a stripped method still runs and receives the default value. Make sure it handles that safely.
  • Constructors and static initializers are not supported. Annotate regular methods only.
  • Reflection, serialization, and dynamic proxies that reference removed members still see the (empty) members.
  • Java compilation only (for now). The plugin hooks JavaCompile tasks. Classes produced by other compilers (e.g. compileKotlin) are not processed yet.
  • Treat this as one layer of a defense-in-depth strategy, alongside licensing, obfuscation, and legal agreements.

Project structure

BypassLogic/
├── bypass-logic-annotations/   # The @BypassLogic annotation
├── bypass-logic-plugin/        # Gradle plugin + ASM visitors
├── bypass-logic-tests/         # Tests for the plugin
├── build.gradle
└── settings.gradle

Key classes in the plugin:

Class Responsibility
BypassLogicPlugin Registers the bypassLogicConfig extension and hooks into JavaCompile.
BypassLogicExtension Plugin configuration (enabled).
AnnotationsCheckerVisitor Fast pre-scan that detects whether a class has @BypassLogic.
BypassLogicClassAdapter ASM class visitor that wraps each method.
BypassLogicMethodAdapter ASM method visitor that emits the default-return bytecode.
SuperClassResolvingWriter ClassWriter that resolves super classes via the project classpath.
types.* Bytecode emitters for primitives, boxed types, and collections.

Building from source

Requirements: JDK 11 or newer (the Gradle wrapper is included).

git clone https://github.com/Assaviv/BypassLogic.git
cd BypassLogic
./gradlew build

Roadmap

  • Publish to the Gradle Plugin Portal / Maven Central
  • Kotlin support (compileKotlin output)
  • Configurable default values per method (e.g. @BypassLogic(returns = ...))
  • Option to throw UnsupportedOperationException instead of returning a default
  • Gradle configuration cache and incremental build support
  • GitHub Actions CI across multiple JDK versions

Contributing

Issues and pull requests are welcome. Please open an issue first to discuss larger changes.

License

Distributed under the Apache License 2.0.

About

The bypass logic project is an open source project to remove code from JVM based languages using an annotation and gradle flag.

Resources

Stars

1 star

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages