- File a (or locate an existing) BinSkim issue that serves as the specification for the rule.
- Create shell rule.
- Prepare test assets for the rule.
- Author standard unit tests.
- Implement the analysis.
- Review impact of new analysis on test set and update baselines if necessary.
- Create a rule request issue in the BinSkim repository by clicking
New issue
and selecting theRule request
template (or just click here). - The issue templates contains additional guidance for authoring a complete rule specification.
- Continue to work with a repository maintainer to iron out details in implementation, etc.
- Rules marked with the
approved
label are ready to implement. - See the CET shadow stack compatibility rule specification for an example.
- Create a new rule id constant for the check in the RuleIds class. The name of the property should match the rule friendly name, e.g.
EnableControlEnforcementTechnologyShadowStack
and its value should be the rule identifier, e.g.,BA2025
. - Open RuleResources and create new user-facing strings for pass and fail conditions. By convention, these strings follow a naming schema that includes the rule identifier, the failure level and an optional additional description of the pass or fail condition, all separated by an underscore, e.g.,
BA2025_Pass
andBA2025_Error
. The string values should be retrieved from the approved rule specific/issue. You can start rules development with placeholder values, obviously. - Add the rule description from the specification issue to a string named as RULEID_FRIENDLYNAME_Decription, e.g.,
BA2025_EnableControlEnforcementTechnologyShadowStack_Description
. Again, you can use a placeholder if this text is still under refinement. - Open the shell rule starter code in BAXXXX.RuleFriendlyName.cs and save a copy to either the [ELFRules] or [PERules] directory, depending on the target platform for the check. Rename the file on save to conform to the actual assigned rule id and friendly rule name, e.g.,
BA2025.EnableControlEnforcementTechnologyShadowStack
. - Find/Replace all occurrences of
BAXXXX
in this file with the rule id. - Find/Replace all occurrences of
RULEFRIENDLYNAME
in this file with the rule name.
- Every rule should be tested against binaries that explicitly fail the check as well as binaries designed to pass.
- Both passing and failing binaries should be updated with other security mitigations. I.e., the goal is for test binaries to be entirely clean in the
pass
case and to only fire results for the new check in therule
case. - Create a directory in the rules functional test directory that matches the rule id and friendly name, separated with a dot character, e.g. BA2025.EnableControlEnforcementTechnologyShadowStack.
- Create directories named Pass and Fail in this directory and copy relevant secure and vulnerable test binaries to their respective location.
By convention, test binary names indicate their language, bittedness/processor, toolchain, and kind, with each attribute separated by an underscore.
Native_x64_VS2019_Console.exe
, for example, indicates a C++ Intel 64-bit console application compiled by the Microsoft Visual Studio 2019 toolchain. - In some cases, it may be useful to create a specific binary to test proper return of the BinSkim
notApplicable
result (which indicates that the binary itself is not a relevant candidate for analysis). For many checks, the standard BinSkim "zoo" of test binaries can be used to verify proper enforcement of applicability.
- Open RuleTestShells.cs and copy the three shell methods into the actual BinSkim RuleTests.cs file.
- Replace
BAXXX
andRULEFRIENDLYNAME
in the test methods with the actual rule id and friendly name, leaving method names such asBA2025_EnableControlEnforcementTechnologyShadowStack_Pass
. - Update the test methods, as per the code comments, to properly configure analysis. This mostly entails configuring checks to understand the applicability of a check to various binary conditions (e.g., whether the binary is 32-bit, an MSIL image, etc.).
- Open the test explorer window and type your rule id and name prefix in the search field, e.g.
BA2025_EnableControlEnforcementTechnologyShadowStack
. You should be able to see your three tests. If you run them, they should fail. :)
How does BinSkim baseline test work?
The binaries to be tested are added in BaselineTestData folder,
when the baseline test run it will dynamically generate .SARIF files in the Actual
folder under it, and compare to Expected folder when the test is running in Windows, or NonWindowsExpected folder otherwise.
Actual
folder is not checked in.
- Prepare test binary with the same naming convention provided in above section ## Prepare test assets.
- Add test binary to BaselineTestData folder.
- In Windows, use PowerShell and
cd
to folder\src\Test.FunctionalTests.BinSkim.Driver
, run.\UpdateBaselines.ps1
. This will create the corresponding .SARIF file in Expected folder. Verify all new or updated .SARIF files if the results are correct. - In Linux (or Windows Subsystem for Linux),
cd
to folder/src/Test.FunctionalTests.BinSkim.Driver
, run./UpdateBaselines.sh
. This will create the corresponding .SARIF file in NonWindowsExpected folder. Verify all new or updated .SARIF files if the results are correct. - Include all new and updated files in your PR. This includes the new binary file itself, the expected .SARIF file for Windows, and the expected .SARIF file for non-Windows. If there are also any updates to the existing .SARIF files for existing binaries, verify the changes are correct and include them in the PR as well.