Recipe Name:
SLF4J Logging: Print Exceptions using SLF4J instead
Description:
Standardise on SLF4J by replacing this call
Level:
marked_information
Language:
  • java
Tags:
  • security
  • SLF4J
  • framework specific
  • logging
Documentation

Throwable.printStacktrace leaks valuable program structure information.

Printing a stack trace gives valuable information about the application's internals, including library/framework names and versions, to an attacker. The method printStaceTrace of java.lang.Throwable will result in valuable program information being printed on standard error. Instead, an appropriate logger, such as SLF4J, should be used.

Before
public void printMessages(Throwable e) {
    e.printStackTrace();
    e.printStackTrace(System.err);
    e.printStackTrace(System.out);
}
After
private final static org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger(LoggingExample.class);

public void printMessages(Throwable e) {
    LOGGER.error("", e);
    LOGGER.error("", e);
    LOGGER.error("", e);
}
Recipe
id: scw:logging:slf4j:printstacktrace
version: 10
metadata:
  name: 'SLF4J Logging: Print Exceptions using SLF4J instead'
  shortDescription: Standardise on SLF4J by replacing this call
  level: marked_information
  language: java
  scwCategory: infoexposure:debuginfo
  cweCategory: 200
  enabled: true
  comment: ""
  descriptionFile: Java/Logging/descriptions/ThrowablePrintStacktraceLeaksInfo.html
  tags: security;SLF4J;framework specific;logging
search:
  methodcall:
    in:
      typeDeclaration:
        anyOf:
        - with:
            child:
              field:
                name: logger
                type: org.slf4j.Logger
        - without:
            child:
              field:
                name: logger
                type:
                  reference:
                    not: org.slf4j.Logger
                  checkInheritance: true
    name: printStackTrace
    type: java.lang.Throwable
availableFixes:
- name: '[OPTIONAL] Replace with SLF4J debug function'
  actions:
  - rewrite:
      to: logger.debug({{{qualifier}}}.toString(), {{{ qualifier }}})
      target: self
  - addField:
      field: private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger({{{ containingClass.containingClass.name }}}.class);
      target: parentClass
- name: ' [RECOMMENDED] Replace with SLF4J error function'
  actions:
  - rewrite:
      to: logger.error({{{qualifier}}}.toString(), {{{ qualifier }}})
      target: self
  - addField:
      field: private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger({{{ containingClass.containingClass.name }}}.class);
      target: parentClass
- name: '[OPTIONAL] Replace with SLF4J info function'
  actions:
  - rewrite:
      to: logger.info({{{qualifier}}}.toString(), {{{ qualifier }}})
      target: self
  - addField:
      field: private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger({{{ containingClass.containingClass.name }}}.class);
      target: parentClass
- name: '[OPTIONAL] Replace with SLF4J warn function'
  actions:
  - rewrite:
      to: logger.warn({{{qualifier}}}.toString(), {{{ qualifier }}})
      target: self
  - addField:
      field: private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger({{{ containingClass.containingClass.name }}}.class);
      target: parentClass
- name: '[OPTIONAL] Replace with SLF4J trace function'
  actions:
  - rewrite:
      to: logger.trace({{{qualifier}}}.toString(), {{{ qualifier }}})
      target: self
  - addField:
      field: private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger({{{ containingClass.containingClass.name }}}.class);
      target: parentClass