Skip to content

Commit

Permalink
Add Makefile, readme and some more code for the Sarif plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
radare committed Feb 29, 2024
1 parent 90567f4 commit bb15dc4
Show file tree
Hide file tree
Showing 3 changed files with 249 additions and 101 deletions.
12 changes: 12 additions & 0 deletions sarif/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
all:
r2 -i sarif.r2.js /bin/ls

install user-install:
mkdir -p $(shell r2 -H R2_USER_PLUGINS)
cp -f sarif.r2.js $(shell r2 -H R2_USER_PLUGINS)

uninstall user-uninstall:
rm -f $(shell r2 -H R2_USER_PLUGINS)/sarif.r2.js

indent:
semistandard --fix sarif.r2.js
24 changes: 24 additions & 0 deletions sarif/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# SARIF for Radare2

Static Analysis Results Interchange Format (SARIF) Version 2.0

## Description

This plugin for radare2 adds the `sarif` command to the r2 shell which allows to import and export SARIF documents (JSON files) into the current session, allowing the analyst to report and visualize the reported vulnerabilities in a binary using a standard file format.

## Usage

```
[0x00000000]> sarif?
sarif [action] [arguments]
sarif help - show this help message
sarif import [file] - import sarif info from given file
sarif export [file] - export sarif findings into given file or stdout
sarif script - generate r2 script with loaded sarif info
sarif reset - reset all loaded sarif reports
[0x00000000]>
```

## Links

* https://docs.oasis-open.org/sarif/sarif/v2.0/sarif-v2.0.html
314 changes: 213 additions & 101 deletions sarif/sarif.r2.js
Original file line number Diff line number Diff line change
@@ -1,115 +1,227 @@
const sarifTemplate = {
"$schema": "http://json.schemastore.org/sarif-2.1.0",
"version": "2.1.0",
"runs": [
$schema: 'http://json.schemastore.org/sarif-2.1.0',
version: '2.1.0',
runs: [
{
"tool": {
"driver": {
"name": "radare2",
"semanticVersion": "1.0.0",
"rules": [
tool: {
driver: {
name: 'radare2',
semanticVersion: '1.0.0',
rules: [
]
}
},
"results": [
results: [
]
}
]
};

class R2Sarif {
constructor() {
this.doc = sarifTemplate;
}
addRule(id, description, helpUri) {
if (this.doc.runs[0].tool.driver.rules.filter((x) => x.id === id).length !== 0) {
return false;
}
const rule = {
"id": id,
"shortDescription": {
"text": description
},
"helpUri": helpUri
}
this.doc.runs[0].tool.driver.rules.push (rule);
return true;
}
addRuleOverflow() {
const ruleId = "VULN-OVERFLOW";
this.addRule(ruleId, "Potential Buffer overflow",
"http://example.com/vulnerability/EXAMPLE-VULN-001");
return ruleId;
}
addResultOverflow(artifact, locations) {
const ruleId = this.addRuleOverflow();
this.addResult (ruleId, "error", "Buffer overflow detected", artifact, locations);
}
addResult(ruleId, level, message, artifact, locations) {
const sarifLocations = [];
const result = {
"ruleId": ruleId,
"level": level,
"message": {
"text": message
},
"locations": []
};
const locationTemplate = {
"physicalLocation": {
"artifactLocation": {
"uri": "binary://" + artifact,
"uriBaseId": "%SRCROOT%"
},
"region": {
"startByteOffset": locations,
"byteLength": 128
}
},
"properties": {
"memoryAddress": "0x0040321A"
}
}
for (let loc of locations) {
const myLoc = locationTemplate;
myLoc.physicalLocation.region = {
startByteOffset: loc.pa,
byteLength: loc.sz,
}
myLoc.properties = {
memoryAddress: loc.va
};
result.locations.push(myLoc);
}
this.doc.runs[0].results.push(result);
}
toString() {
return JSON.stringify(this.doc);
}
toScript() {
let script = "";
const results = this.doc.runs[0].results;
let counter = 0;
for (let res of results) {
const text = res.message.text;
for (let loc of res.locations) {
console.log(JSON.stringify(res));
const address = loc.properties.memoryAddress;
const size = loc.physicalLocation.region.byteLength;
script += `CC ${text} @ ${address}\n`;
script += `f bug.${counter} ${size} ${address}\n`;
counter++;
}
}
return script;
}
}
constructor () {
this.doc = sarifTemplate;
}

addRule (id, description, helpUri) {
if (this.doc.runs[0].tool.driver.rules.filter((x) => x.id === id).length !== 0) {
return false;
}
const rule = {
id: id,
shortDescription: {
text: description
},
helpUri: helpUri
};
this.doc.runs[0].tool.driver.rules.push(rule);
return true;
}

reset () {
this.doc.runs[0].results = [];
this.doc.runs[0].tool.driver.rules = [];
}

addRuleOverflow () {
const ruleId = 'VULN-OVERFLOW';
this.addRule(ruleId, 'Potential Buffer Overflow',
'http://example.com/vulnerability/EXAMPLE-VULN-001');
return ruleId;
}

addRuleWeakCrypto () {
const ruleId = 'VULN-WEAKCRYPTO';
this.addRule(ruleId, 'Weak Crypto Usage',
'http://example.com/vulnerability/EXAMPLE-VULN-002');
return ruleId;
}

addResultWeakCrypto (artifact, comment, locations) {
const ruleId = this.addRuleWeakCrypto();
this.addResult(ruleId, 'error', comment, artifact, locations);
}

const s = new R2Sarif();
s.addResultOverflow('/bin/ls', [
{ va: 0x804804, pa: 0x804, sz: 32 }
]);
addResultOverflow (artifact, comment, locations) {
const ruleId = this.addRuleOverflow();
this.addResult(ruleId, 'error', comment, artifact, locations);
}

console.log(s.toString());
console.log(s.toScript());
addResult (ruleId, level, message, artifact, locations) {
const sarifLocations = [];
const result = {
ruleId: ruleId,
level: level,
message: {
text: message
},
locations: []
};
const locationTemplate = {
physicalLocation: {
artifactLocation: {
uri: 'binary://' + artifact,
uriBaseId: '%SRCROOT%'
},
region: {
startByteOffset: locations,
byteLength: 128
}
},
properties: {
memoryAddress: '0x0040321A'
}
};
for (const loc of locations) {
const myLoc = locationTemplate;
myLoc.physicalLocation.region = {
startByteOffset: loc.pa,
byteLength: loc.sz
};
myLoc.properties = {
memoryAddress: loc.va
};
result.locations.push(myLoc);
}
this.doc.runs[0].results.push(result);
}

toString () {
return JSON.stringify(this.doc, null, 2) + '\n';
}

toScript () {
let script = '# r2sarif script\n';
const results = this.doc.runs[0].results;
let counter = 0;
for (const res of results) {
const text = res.message.text;
for (const loc of res.locations) {
console.log(JSON.stringify(res));
const address = loc.properties.memoryAddress;
const size = loc.physicalLocation.region.byteLength;
script += `CC ${text} @ ${address}\n`;
script += `f bug.${counter} ${size} ${address}\n`;
counter++;
}
}
return script;
}
}

function sarifTest () {
const s = new R2Sarif();
s.addResultOverflow('/bin/ls', 'buffer overflow detected', [
{ va: 0x804804, pa: 0x804, sz: 32 }
]);
console.log(s.toString());
console.log(s.toScript());
}

function sarifRegisterPlugin () {
const sarif = new R2Sarif();
function sarifCommand (args) {
function sarifHelp () {
console.log('sarif [action] [arguments]');
console.log('sarif help - show this help message');
console.log('sarif import [file] - import sarif info from given file');
console.log('sarif export [file] - export sarif findings into given file or stdout');
console.log('sarif script - generate r2 script with loaded sarif info');
console.log('sarif reset - reset all loaded sarif reports');
}
function sarifImport (fileName) {
console.log('Importing from ' + fileName);
}
function sarifExport (fileName) {
console.log(fileName);
if (fileName === '') {
console.log(sarif.toString());
} else {
console.log('Exporting to ' + fileName);
}
}
function sarifScript (fileName) {
console.log(sarif.toScript());
}
function sarifAdd (args) {
if (args === "") {
console.log("");
}
console.log(args);
}
let arg = args.substr('sarif'.length).trim();
const space = arg.indexOf(' ');
let action = arg.trim();
if (space !== -1) {
action = arg.substr(0, space);
arg = arg.substr(space + 1);
} else {
arg = '';
}
switch (action) {
case '':
case '?':
case '-h':
case 'help':
sarifHelp();
break;
case '-a':
case 'add':
sarifAdd(arg);
break;
case '-i':
case 'import':
sarifImport(arg);
break;
case '-e':
case 'export':
sarifExport(arg);
break;
case '*':
case '-r':
case 'r2':
case 'script':
sarifScript(arg);
break;
default:
console.error('Unknown action');
break;
}
}
r2.unload('core', 'sarif');
r2.plugin('core', function () {
function coreCall (cmd) {
if (cmd.startsWith('sarif')) {
sarifCommand(cmd);
return true;
}
return false;
}
return {
name: 'sarif',
license: 'MIT',
desc: 'support importing and exporting sarif format',
call: coreCall
};
});
}
sarifRegisterPlugin();

0 comments on commit bb15dc4

Please sign in to comment.