Skip to content

Commit

Permalink
introduce brakeman producer
Browse files Browse the repository at this point in the history
  • Loading branch information
northdpole committed Sep 16, 2024
1 parent 68faead commit 1116513
Show file tree
Hide file tree
Showing 5 changed files with 597 additions and 0 deletions.
9 changes: 9 additions & 0 deletions components/producers/brakeman/examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Examples

## `dvra.json`

Using [the dvra repo](https://github.com/guilleiguaran/dvra).

```bash
go run ../main.go -in ./examples/guilleiguaran/dvra.json -out ./out-dvra.pb
```
156 changes: 156 additions & 0 deletions components/producers/brakeman/examples/guilleiguaran/dvra.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
{
"scan_info": {
"app_path": "/code",
"rails_version": "4.x",
"security_warnings": 2,
"start_time": "2024-09-16 14:54:55 +0000",
"end_time": "2024-09-16 14:54:55 +0000",
"duration": 0.267453374,
"checks_performed": [
"BasicAuth",
"BasicAuthTimingAttack",
"CSRFTokenForgeryCVE",
"ContentTag",
"CookieSerialization",
"CreateWith",
"CrossSiteScripting",
"DefaultRoutes",
"Deserialize",
"DetailedExceptions",
"DigestDoS",
"DivideByZero",
"DynamicFinders",
"EOLRails",
"EOLRuby",
"EscapeFunction",
"Evaluation",
"Execute",
"FileAccess",
"FileDisclosure",
"FilterSkipping",
"ForceSSL",
"ForgerySetting",
"HeaderDoS",
"I18nXSS",
"JRubyXML",
"JSONEncoding",
"JSONEntityEscape",
"JSONParsing",
"LinkTo",
"LinkToHref",
"MailTo",
"MassAssignment",
"MimeTypeDoS",
"ModelAttrAccessible",
"ModelAttributes",
"ModelSerialize",
"NestedAttributes",
"NestedAttributesBypass",
"NumberToCurrency",
"PageCachingCVE",
"Pathname",
"PermitAttributes",
"QuoteTableName",
"Ransack",
"Redirect",
"RegexDoS",
"Render",
"RenderDoS",
"RenderInline",
"ResponseSplitting",
"ReverseTabnabbing",
"RouteDoS",
"SQL",
"SQLCVEs",
"SSLVerify",
"SafeBufferManipulation",
"SanitizeConfigCve",
"SanitizeMethods",
"Secrets",
"SelectTag",
"SelectVulnerability",
"Send",
"SendFile",
"SessionManipulation",
"SessionSettings",
"SimpleFormat",
"SingleQuotes",
"SkipBeforeFilter",
"SprocketsPathTraversal",
"StripTags",
"SymbolDoS",
"SymbolDoSCVE",
"TemplateInjection",
"TranslateBug",
"UnsafeReflection",
"UnsafeReflectionMethods",
"UnscopedFind",
"ValidationRegex",
"VerbConfusion",
"WeakHash",
"WeakRSAKey",
"WithoutProtection",
"XMLDoS",
"YAMLParsing"
],
"number_of_controllers": 4,
"number_of_models": 2,
"number_of_templates": 11,
"ruby_version": "3.3.4",
"brakeman_version": "6.2.1"
},
"warnings": [
{
"warning_type": "SQL Injection",
"warning_code": 0,
"fingerprint": "2377c284794a5ea965ede6303a2236ed4eb829642879477f30bdf94ad0f11054",
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "dvra/app/controllers/sessions_controller.rb",
"line": 8,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "User.find_by_sql(\"SELECT * FROM users WHERE email = '#{params[:email]}' AND password = '#{params[:password]}'\")",
"render_path": null,
"location": {
"type": "method",
"class": "SessionsController",
"method": "create"
},
"user_input": "params[:email]",
"confidence": "High",
"cwe_id": [
89
]
},
{
"warning_type": "Cross-Site Request Forgery",
"warning_code": 7,
"fingerprint": "8a1e3382d5e2bbbf94c19f5ad1cb9355df95f9231f9f426803ac8005b4a63c0b",
"check_name": "ForgerySetting",
"message": "`protect_from_forgery` should be called in `ApplicationController`",
"file": "dvra/app/controllers/application_controller.rb",
"line": 1,
"link": "https://brakemanscanner.org/docs/warning_types/cross-site_request_forgery/",
"code": null,
"render_path": null,
"location": {
"type": "controller",
"controller": "ApplicationController"
},
"user_input": null,
"confidence": "High",
"cwe_id": [
352
]
}
],
"ignored_warnings": [

],
"errors": [

],
"obsolete": [

]
}
127 changes: 127 additions & 0 deletions components/producers/brakeman/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package main

import (
"encoding/json"
"fmt"
"log"
"log/slog"

v1 "github.com/ocurity/dracon/api/proto/v1"
"github.com/ocurity/dracon/pkg/context"

"github.com/ocurity/dracon/components/producers"
)

func main() {
if err := producers.ParseFlags(); err != nil {
log.Fatal(err)
}

inFile, err := producers.ReadInFile()
if err != nil {
log.Fatal(err)
}

var results BrakemanOut
if err := json.Unmarshal(inFile, &results); err != nil {
log.Fatal(err)
}

issues, err := parseIssues(&results)
if err != nil {
log.Fatal(err)
}
if err := producers.WriteDraconOut(
"brakeman",
issues,
); err != nil {
log.Fatal(err)
}
}

func handleLine(line string) (int, int) {
// can be both "line" or "line-line"
var start, end int
_, err := fmt.Sscanf(line, "%d-%d", &start, &end)
if err != nil {
_, err := fmt.Sscanf(line, "%d", &start)
if err != nil {
slog.Warn("Failed to parse line", "line", line)
}
end = start
}
return start, end
}

func parseIssues(out *BrakemanOut) ([]*v1.Issue, error) {
issues := []*v1.Issue{}
for _, r := range out.Warnings {
start, end := handleLine(fmt.Sprintf("%d", r.Line))
cwe := []int32{}
for _, c := range r.CweID {
cwe = append(cwe, int32(c))
}
iss := &v1.Issue{
Target: producers.GetFileTarget(r.File, start, end),
Type: fmt.Sprintf("%s:%d", r.WarningType, r.WarningCode),
Title: r.Message,
Severity: v1.Severity_SEVERITY_UNSPECIFIED,
Cvss: 0.0,
Cwe: cwe,
Confidence: v1.Confidence(v1.Confidence_value[fmt.Sprintf("CONFIDENCE_%s", r.Confidence)]),
Description: fmt.Sprintf("%s\n%s\n", r.Message, r.WarningType),
}

// Extract the code snippet, if possible
code, err := context.ExtractCodeFromFileTarget(iss.Target)
if err != nil {
slog.Warn("Failed to extract code snippet", "error", err)
code = ""
}
iss.ContextSegment = &code

issues = append(issues, iss)
}
return issues, nil
}

type ScanInfo struct {
AppPath string `json:"app_path,omitempty"`
RailsVersion string `json:"rails_version,omitempty"`
SecurityWarnings int `json:"security_warnings,omitempty"`
StartTime string `json:"start_time,omitempty"`
EndTime string `json:"end_time,omitempty"`
Duration float64 `json:"duration,omitempty"`
ChecksPerformed []string `json:"checks_performed,omitempty"`
NumberOfControllers int `json:"number_of_controllers,omitempty"`
NumberOfModels int `json:"number_of_models,omitempty"`
NumberOfTemplates int `json:"number_of_templates,omitempty"`
RubyVersion string `json:"ruby_version,omitempty"`
BrakemanVersion string `json:"brakeman_version,omitempty"`
}
type BrakemanLocation struct {
Type string `json:"type,omitempty"`
Class string `json:"class,omitempty"`
Method string `json:"method,omitempty"`
Controller string `json:"controller,omitempty"`
}
type BrakemanWarning struct {
WarningType string `json:"warning_type,omitempty"`
WarningCode int `json:"warning_code,omitempty"`
Fingerprint string `json:"fingerprint,omitempty"`
CheckName string `json:"check_name,omitempty"`
Message string `json:"message,omitempty"`
File string `json:"file,omitempty"`
Line int `json:"line,omitempty"`
Link string `json:"link,omitempty"`
Code string `json:"code,omitempty"`
RenderPath any `json:"render_path,omitempty"`
Location BrakemanLocation `json:"location,omitempty"`
UserInput string `json:"user_input,omitempty"`
Confidence string `json:"confidence,omitempty"`
CweID []int `json:"cwe_id,omitempty"`
}
type BrakemanOut struct {
ScanInfo ScanInfo `json:"scan_info,omitempty"`
Warnings []BrakemanWarning `json:"warnings,omitempty"`
}
Loading

0 comments on commit 1116513

Please sign in to comment.