iTranslated by AI
Building a GitHub Action to Prompt for Ignore Reasons in Brakeman Static Analysis
Hello. I'm bun913, a die-hard fan of Dragon Quest: The Adventure of Dai.
Are you all performing static analysis? For languages like TypeScript and JavaScript, tools like ESLint and TSLint are well-known, and for Ruby, RuboCop is a popular choice.
Brakeman is another static analysis tool for Ruby that focuses on detecting code with potential vulnerabilities.
However, since static analysis doesn't observe runtime behavior, not all warnings are necessarily actual problems. You may often judge them as false positives (where a warning is issued even though there's no real issue).
That said, ignore settings can accumulate, and you might lose track of why they were added, or you might intend to fix them later but simply forget.
To address this, I've created a custom GitHub Action that encourages writing a reason when ignoring Brakeman warnings.
The Tool I Created
I have published it in the following repository:
By calling it in GitHub Actions as shown below, it prompts for a reason for each ignore entry in the specified Brakeman ignore files.
jobs:
lint-workflow:
name: Check dist/
runs-on: ubuntu-latest
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
- name: Brakeman ignore reason check
id: valid-json
# Please specify the latest version
uses: bun913/please-write-ignore-reason@v0.9.2
with:
# ↓ Multiple files can be specified if needed
fileListString: samples/brakeman.ignore,samples/valid2.json
If a reason is not provided in the specified file, or if a non-existent file is specified, an error will be output as shown below:

How it Works
The behavior of this tool is quite simple. For a brakeman.ignore file like the one below, it outputs an error if the note property of any element under ignored_warnings is less than 5 characters or if the file does not exist.
{
"ignored_warnings": [
{
"warning_type": "Remote Code Execution",
"warning_code": 25,
"fingerprint": "006ac5fe3834bf2e73ee51b67eb111066f618be46e391d493c541ea2a906a82f",
"check_name": "Deserialize",
"message": "`Oj.load` called with parameter value",
"file": "app/controllers/users_controller.rb",
"line": 52,
"link": "https://brakemanscanner.org/docs/warning_types/unsafe_deserialization",
"code": "Oj.load(params[:json], :mode => :object)",
"render_path": null,
"location": {
"type": "method",
"class": "UsersController",
"method": "some_api"
},
"user_input": "params[:json]",
"confidence": "High",
// ↓ Error because it's less than 5 characters
"note": ""
},
{
"warning_type": "Remote Code Execution",
"warning_code": 25,
"fingerprint": "3bc375c9cb79d8bcd9e7f1c09a574fa3deeab17f924cf20455cbd4c15e9c66eb",
"check_name": "Deserialize",
"message": "`Oj.object_load` called with parameter value",
"file": "app/controllers/users_controller.rb",
"line": 53,
"link": "https://brakemanscanner.org/docs/warning_types/unsafe_deserialization",
"code": "Oj.object_load(params[:json], :mode => :strict)",
"render_path": null,
"location": {
"type": "method",
"class": "UsersController",
"method": "some_api"
},
"user_input": "params[:json]",
"confidence": "High",
// Error because it's less than 5 characters
"note": "a"
},
{
"warning_type": "Remote Code Execution",
"warning_code": 25,
"fingerprint": "97ecaa5677c8eadaed09217a704e59092921fab24cc751e05dfb7b167beda2cf",
"check_name": "Deserialize",
"message": "`Oj.load` called with parameter value",
"file": "app/controllers/users_controller.rb",
"line": 51,
"link": "https://brakemanscanner.org/docs/warning_types/unsafe_deserialization",
"code": "Oj.load(params[:json])",
"render_path": null,
"location": {
"type": "method",
"class": "UsersController",
"method": "some_api"
},
"user_input": "params[:json]",
"confidence": "High",
// ↓ This is OK
"note": "Ref: https://example.com/issue/45"
}
],
"updated": "2019-04-02 12:15:05 -0700",
"brakeman_version": "4.5.0"
}
The logic is written in TypeScript, and the validation is implemented using a library called Valibot.
By using Valibot to parse the JSON file, the structure and types of the data are automatically verified, which makes it easier to ensure type safety. (Simply using type assertions like as is insufficient for proper type checking.)
If you want to create a GitHub Action using TypeScript, you can get started quickly with the following Public Template. The usage is well-documented in the README and very easy to follow:
Future Outlook and Thoughts
Future Plans
- I want to make it available for CircleCI and other platforms, not just GitHub Actions.
- In the following scrap, I documented my attempt to use a tool called Dagger to enable its use in CircleCI and elsewhere.
- I ended up giving up on it to ensure I could continue maintenance easily. You can really feel the reality of development here, right?
- The journey to writing a CI that can also be used with GitHub Actions using Dagger
- I (might) want to support other static analysis tools besides Brakeman.
- Make the rules more flexible.
- I've currently mandated a minimum of 5 characters for the reason, but I feel that might be too lenient or a bit vague.
- I roughly settled on 5 characters considering cases where someone might just post a link like
Ref:#45.
- I roughly settled on 5 characters considering cases where someone might just post a link like
- I've currently mandated a minimum of 5 characters for the reason, but I feel that might be too lenient or a bit vague.
While it might be possible to use generative AI to check the validity of the reasons, I don't believe such a feature is necessary at this stage.
I envision this primarily as a tool for preventing accidental oversights and as a starting point for teams to discuss their current ignore reasons.
Thoughts
Most importantly, since my goal was to quickly build and release something within a few hours, the implementation is very simple.
I went ahead with the spirit of Better than Nothing, believing that once it's public, someone kind might help improve it.
It's a very niche tool, but I plan to keep improving it to make it easier to use.
Thank you for reading to the end!
Discussion