블로그

How to catch and fix a Guice dependency injection issue using Sensei

January 25, 2021
앨런 리처드슨

The Sensei project itself has its own set of recipes that have built up over time. This blog post is an example of one of the scenarios the Sensei team built a recipe for. A misconfiguration of Guice, which led to a NullPointerException being reported at runtime during testing.

This can be generalized to many Dependency Injection scenarios where code is syntactically correct, but because the wiring configuration was incorrect, an error slips through.

This often happens when we are learning the technology, and we keep making the simple mistake of forgetting to wire things up. But this also happens to experienced professionals because, well... we all make mistakes, and we may not have Unit Tests to cover everything.

 

RunTime Exceptions from Incorrect Dependency Injection Wiring

The code below fails at runtime with a NullPointerException.

‍

injector = Guice.createInjector(new SystemOutModule());
CountReporter reporter = injector.getInstance(CountReporter.class);
String [] lines5 = {"1: line", "2: line", "3: line", "4: line", "5: line"};
reporter.reportThisMany(Arrays.asList(lines5));
Assertions.assertEquals(5, reporter.getCount());


The code is syntactically correct but fails because we missed out a requestStaticInjection in our SystemOutModule configuration.

‍

public class SystemOutModule extends AbstractModule {
    @Override
    protected void configure() {
        binder().bind(ILineReporter.class).to(SystemOutReporter.class);
    }
}


When we try to use the reporter, created using the Injector, it is not fully instantiated and we receive a NullPointerException when we call reportThisMany.

We may well have missed that in our code review, or we didn't have unit tests that triggered the dependency injection, and it slipped out into our build.

‍

Warning Signs

In this case, there is a warning sign, the CountReporter has a static field annotated with @Inject but... the CountReporter class itself is package private.

In a complicated code base this could be a warning sign that the code is incorrect because the Module class configuring the bindings needs to be in the same package for this to work.

‍

class CountReporter {
    @Inject
    private static ILineReporter reporter;


Another error that we made, which we might have picked up in a code review, is that we forgot to actually bind the fields in the SystemOutModule configure method.

binder().requestStaticInjection(CountReporter.class);


Had we written the requestStaticInjection code then the Syntax Error generated when trying to use the CountReporter would have alerted us to the simple error.

> 'reporters.CountReporter' is not public in 'reporters'. Cannot be accessed from outside package

‍

Sadly. We forgot, and there were no syntactic warning signs in the code.

How could Sensei help?

We probably wouldn't use Sensei to pick up the missing requestStaticInjection since all our Guice configuration wiring would need to use that method, and we can't guarantee that all wiring is going to be as simple as this use-case.

We could write a Sensei rule to look for some warning signs that our code is not up to scratch.

In this case that would mean:

  • Find any classes with @Inject annotated fields
  • Where the classes are not public.

The above was the warning sign that they were unlikely to have been wired up.

By creating a recipe, then we will have a warning sign early, during the coding, and reduce the reliance on our pull requests or resolving our tech debt to allow us to add Unit Tests.

How to create a recipe?

The task I want to complete is:

  • Create a recipe that matches fields annotated with @Inject which are in protected private classes

That should hopefully give us enough warning to identify any Modules using it and add the missing wiring code.

In my CountReporter class, I will use Alt+Enter to Create a new Recipe and I will start from scratch

I will name this and add a description:

Name: Guice: Injected Field Not Public
Description: If the Injected field is not public then the code might not be wired up
Level: Warning


The search I write looks for a class with a field annotated as Inject but which has not been scoped as public.

search:
  field:
    with:
      annotation:
        type: "com.google.inject.Inject"
    in:
      class:
        without:
          modifier: "public"

Fix

The QuickFix in the recipe will amend the injected class by changing the scope. But that is not the only code I have to change.

availableFixes:

- name: "Change class to public. Remember to also request injection on this class"
  actions:
  - changeModifiers:
      visibility: "public"
      target: "parentClass"
Sensei edit recipe QuickFix settings

When the recipe is triggered I still have a manual step to perform in my code, adding the line containing requestStaticInjection to fully instantiate the object.

public class SystemOutModule extends AbstractModule {
    @Override
    protected void configure() {
        binder().bind(ILineReporter.class).to(SystemOutReporter.class);
        // instantiate via dependency injection
        binder().requestStaticInjection(CountReporter.class);
    }
}


I could potentially write another recipe to pick this up. I probably wouldn't do that unless forgetting to add the static injection became a semi-regular error that I made when coding.

‍

Summary

If we ever find ourselves making a mistake with a common root pattern, then Sensei can help codify the knowledge around detecting and fixing the issue, and then hopefully, it won't slip through code reviews and into production.

Sometimes the recipes we write identify heuristic patterns i.e. matching them doesn't guarantee that there is a problem, but is likely that there is a problem.

Also, the recipes and QuickFixes that we write, don't have to be fully comprehensive, they need to be good enough that they help us identify and fix problems without being overcomplicated. Because when they become overcomplicated they become harder to understand and harder to maintain.

---


You can install Sensei from within IntelliJ using "Preferences \ Plugins" (Mac) or "Settings \ Plugins" (Windows) then just search for "sensei secure code".


The source code and recipes for this post can be found in the `sensei-blog-examples` repository in the Secure Code Warrior GitHub account, in the `guiceexamples` module.


https://github.com/securecodewarrior/sensei-blog-examples

Learn more about Sensei



태그라인

배포 전 AI 기반 개발 거버넌스 확보

AI 기반 위험을 측정하고, 커밋 시점에 보안 코딩 정책을 적용하며, SDLC 전반에 걸쳐 안전한 소프트웨어 제공 속도를 높이세요.

데모 예약하기
리소스 라이브러리

더 많은 블로그 살펴보기

보안 코딩, AI 거버넌스, 소프트웨어 리스크 관리에 관한 전문가 콘텐츠를 확인해 보세요.

전체 보기
사례 연구
필터 라벨
div 블록 내부의 텍스트입니다.

Supercharged Security Awareness: How Tournaments are Inspiring Developers at Erste Group

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

더 알아보기
사례 연구
필터 라벨
div 블록 내부의 텍스트입니다.

Security as culture: How Blue Prism cultivates world-class secure developers

Learn how Blue Prism, the global leader in intelligent automation for the enterprise, used Secure Code Warrior's agile learning platform to create a security-first culture with their developers, achieve their business goals, and ship secure code at speed

더 알아보기
사례 연구
필터 라벨
div 블록 내부의 텍스트입니다.

하나의 보안 문화: Sage는 어떻게 챔피언 프로그램을 구축했는가

Sage가 유연하고 관계 중심적인 접근 방식을 통해 보안을 강화하고, 200명 이상의 보안 챔피언을 양성하며 실질적인 위험 감소를 달성한 사례를 확인해 보세요.

더 알아보기
블로그
필터 라벨
div 블록 내부의 텍스트입니다.

2026년 사이버 보안 인식의 달: 기업 방어의 핵심, AI 보안 이해하기

주요 AI 기업 대부분이 안전 가이드라인이 마련될 때까지 AI 개발 속도를 전 세계적으로 늦춰야 한다고 공개적으로 촉구하는 시점에 이르렀지만, 사실 심각한 보안 문제는 이미 현실화되었습니다. 이로 인해 AI 도입에 따른 금전적 비용과 위험 부담이 계속 쌓이면서 수많은 보안 책임자와 경영진이 절박한 상황에 놓였습니다.

더 알아보기
블로그
필터 라벨
div 블록 내부의 텍스트입니다.

인에이블러 8: 프로그램 브랜딩

성공적인 보안 코딩 프로그램을 위해서는 훌륭한 콘텐츠 그 이상의 것이 필요합니다. 인에이블러 8에서는 브랜딩을 통해 참여를 누구나 선망하는 위상 중심의 경험으로 바꾸는 방법을 알아봅니다.

더 알아보기
블로그
필터 라벨
div 블록 내부의 텍스트입니다.

이제 모든 직원이 AI 사이버 보안의 최전선에 있습니다

엔터프라이즈 기술 환경은 너무 늦기 전까지는 아무도 예상하지 못한 속도로 변화하고 있습니다. 우리는 공식적으로 사람이 직접 작성한 코드와 기본적인 코파일럿 지원의 시대를 지나 '에이전트 기반 개발 수명 주기(ADLC)'의 시대로 진입했습니다. 자율형 AI 에이전트는 여러 기능 전반에 걸쳐 전례 없는 효율성을 약속하지만, 동시에 완전히 새로운 차원의 보안 및 규제 위험을 야기합니다.

더 알아보기

배포 전 AI 기반 개발 보안 확보

소프트웨어 개발 수명 주기 전반에서 개발자 리스크를 파악하고, 정책을 시행하며, 취약점을 예방하세요.

데모 예약하기
항목이 없습니다.