Skip to content

Commit

Permalink
feat: add support for sarif format
Browse files Browse the repository at this point in the history
  • Loading branch information
hugo-syn committed Oct 27, 2024
1 parent 4ac9edf commit 3037b93
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 5 deletions.
32 changes: 28 additions & 4 deletions cmd/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ Options:
-v, --version
-d, --debug
--verbose
--json JSON output
--oneline Use one line per one error. Useful for reading error messages from programs
--format <format> Output format, json, sarif or custom template to format error messages in Go template syntax. See https://github.com/rhysd/actionlint/tree/main/docs/usage.md#format
--oneline Use one line per one error. Useful for reading error messages from programs
Args:
<target> Target File or directory to scan
Expand Down Expand Up @@ -89,6 +89,25 @@ func runScanner(args docopt.Opts) int {
return common.ExitStatusFailure
}

if args["--format"] != nil {
switch format := args["--format"].(string); format {
case "json":
opts.Format = "{{json .}}"
case "sarif":
opts.Format = string(common.SarifTemplate)
default:
opts.Format = format
}
// Now we can use our own formatter on all the errors.
err = core.DisplayErrors(os.Stdout, opts.Format, errs)
}

if err != nil {
common.Log.Error(err)

return common.ExitStatusFailure
}

if len(errs) > 0 {
return common.ExitStatusSuccessProblemFound // Linter found some issues, yay!
}
Expand Down Expand Up @@ -157,8 +176,13 @@ func setScannerArgs(args docopt.Opts) actionlint.LinterOptions {
opts.Oneline = true
}

if v, _ := args.Bool("--json"); v {
opts.Format = "{{json .}}"
/*
This is a hacky trick to disable the formatter of actionlint otherwise it will use
a new formatter for each repo and this not what we want. We want to use the same
formatter for all the repo. It's far from being perfect but I don't know how to do it.
*/
if args["--format"] != nil {
opts.Format = "{{\"\"}}"
}

setCoreParameter(args)
Expand Down
66 changes: 66 additions & 0 deletions common/assets/sarif.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [
{
"tool": {
"driver": {
"name": "octoscan",
"version": {{ getVersion | json }},
"informationUri": "https://github.com/synacktiv/octoscan",
"rules": [
{{$first := true}}
{{range $ := allKinds }}
{{if $first}}{{$first = false}}{{else}},{{end}}
{
"id": {{json $.Name}},
"name": {{$.Name | toPascalCase | json}},
"defaultConfiguration": {
"level": "error"
},
"properties": {
"description": {{json $.Description}},
"queryURI": "https://github.com/synacktiv/octoscan?tab=readme-ov-file#rules"
},
"fullDescription": {
"text": {{json $.Description}}
},
"helpUri": "https://github.com/synacktiv/octoscan?tab=readme-ov-file#rules"
}
{{end}}
]
}
},
"results": [
{{$first := true}}
{{range $ := .}}
{{if $first}}{{$first = false}}{{else}},{{end}}
{
"ruleId": {{json $.Kind}},
"message": {
"text": {{json $.Message}}
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": {{json $.Filepath}},
"uriBaseId": "%SRCROOT%"
},
"region": {
"startLine": {{$.Line}},
"startColumn": {{$.Column}},
"endColumn": {{$.EndColumn}},
"snippet": {
"text": {{json $.Snippet}}
}
}
}
}
]
}
{{end}}
]
}
]
}
14 changes: 14 additions & 0 deletions common/helpers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package common

import (
_ "embed"
"net/http"
"os"
"regexp"
Expand Down Expand Up @@ -49,6 +50,16 @@ var SyntaxCheckErrors = []string{
"sequence node but mapping node is expected",
"please remove this section if it's unnecessary",
"is only available for a reusable workflow call with",
"previously defined at line",
"could not parse as YAML",
"it must be one of",
"event must be one of",
"this step is for running shell command since it contains",
"job is scalar node but mapping node is expected",
"section should have",
"but found plain text node",
"but found mapping node with",
"section must be mapping",
}

var TriggerWithExternalData = []string{
Expand Down Expand Up @@ -97,6 +108,9 @@ var AllTriggers = []string{
"workflow_run",
}

//go:embed assets/sarif.template
var SarifTemplate []byte

func IsDirectory(path string) bool {
fileInfo, err := os.Stat(path)
if err != nil {
Expand Down
30 changes: 29 additions & 1 deletion core/linter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package core

import (
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
Expand Down Expand Up @@ -130,7 +131,7 @@ func offlineRules() []actionlint.Rule {
}

if RulesSwitch["debug-artefacts"] {
res = append(res, rules.NewRuleRuleDebugArtefacts())
res = append(res, rules.NewRuleRuleDebugArtefacts(FilterTriggers))
}

if RulesSwitch["debug-js-exec"] {
Expand Down Expand Up @@ -174,3 +175,30 @@ func (l *OctoLinter) LintRepositoryRecurse(dir string) ([]*actionlint.Error, err
}
return lintErrors, nil
}

/*
Everything is stolen from here: https://github.com/rhysd/actionlint/blob/main/docs/usage.md#format
Again thanks @rhysd for the great work.
*/
func DisplayErrors(writer io.Writer, format string, errs []*actionlint.Error) error {
formatter, err := actionlint.NewErrorFormatter(format)
rules := &[]actionlint.Rule{}
availableRules := OnRulesCreated(*rules)

for _, rule := range availableRules {
formatter.RegisterRule(rule)
}

if err != nil {
return err
}

temp := make([]*actionlint.ErrorTemplateFields, 0, len(errs))

for _, octoscanErr := range errs {
src, _ := os.ReadFile(octoscanErr.Filepath)
temp = append(temp, octoscanErr.GetTemplateFields(src))
}

return formatter.Print(writer, temp)
}

0 comments on commit 3037b93

Please sign in to comment.