@@ -2,29 +2,25 @@ package main
22
33import  (
44	"encoding/json" 
5+ 	"flag" 
56	"fmt" 
67	"io/ioutil" 
78	"os" 
89	"strings" 
910)
1011
11- // ----------- Sysdig structs (simplified; adjust fields to match your actual JSON) ----------- 
12- 
1312type  Report  struct  {
1413	Info    Info    `json:"info"` 
1514	Result  Result  `json:"result"` 
1615}
17- 
1816type  Info  struct  {
1917	ResultUrl  string  `json:"resultUrl"` 
2018	ResultId   string  `json:"resultId"` 
2119}
22- 
2320type  Result  struct  {
2421	Metadata  Metadata   `json:"metadata"` 
2522	Packages  []Package  `json:"packages"` 
2623}
27- 
2824type  Metadata  struct  {
2925	PullString    string  `json:"pullString"` 
3026	Digest        string  `json:"digest"` 
@@ -35,7 +31,6 @@ type Metadata struct {
3531	Size          int     `json:"size"` 
3632	LayersCount   int     `json:"layersCount"` 
3733}
38- 
3934type  Package  struct  {
4035	Name          string  `json:"name"` 
4136	Version       string  `json:"version"` 
@@ -44,37 +39,32 @@ type Package struct {
4439	SuggestedFix  string  `json:"suggestedFix"` 
4540	Vulns         []Vuln  `json:"vulns"` 
4641}
47- 
4842type  Vuln  struct  {
49- 	Name            string     `json:"name"` 
50- 	Severity        Severity   `json:"severity"` 
51- 	CvssScore       CvssScore  `json:"cvssScore"` 
52- 	Exploitable     bool       `json:"exploitable"` 
53- 	FixedInVersion  string     `json:"fixedInVersion"` 
43+ 	Name            string         `json:"name"` 
44+ 	Severity        Severity       `json:"severity"` 
45+ 	CvssScore       CvssScore      `json:"cvssScore"` 
46+ 	Exploitable     bool           `json:"exploitable"` 
47+ 	FixedInVersion  string         `json:"fixedInVersion"` 
48+ 	AcceptedRisks   []interface {} `json:"acceptedRisks"` 
5449}
55- 
5650type  Severity  struct  {
5751	Value  string  `json:"value"` 
5852}
59- 
6053type  CvssScore  struct  {
6154	Value  CvssValue  `json:"value"` 
6255}
63- 
6456type  CvssValue  struct  {
6557	Score    float64  `json:"score"` 
6658	Version  string   `json:"version"` 
6759	Vector   string   `json:"vector"` 
6860}
6961
70- // ----------- SARIF structs (essential subset) ----------- 
71- 
62+ // SARIF structures 
7263type  SARIF  struct  {
7364	Schema   string      `json:"$schema"` 
7465	Version  string      `json:"version"` 
7566	Runs     []SARIFRun  `json:"runs"` 
7667}
77- 
7868type  SARIFRun  struct  {
7969	Tool  struct  {
8070		Driver  struct  {
@@ -92,13 +82,11 @@ type SARIFRun struct {
9282	ColumnKind        string                  `json:"columnKind"` 
9383	Properties        map [string ]interface {} `json:"properties"` 
9484}
95- 
9685type  LogicalLocation  struct  {
9786	Name                string  `json:"name"` 
9887	FullyQualifiedName  string  `json:"fullyQualifiedName"` 
9988	Kind                string  `json:"kind"` 
10089}
101- 
10290type  SARIFRule  struct  {
10391	Id                string           `json:"id"` 
10492	Name              string           `json:"name"` 
@@ -108,46 +96,113 @@ type SARIFRule struct {
10896	Help              SARIFHelp        `json:"help"` 
10997	Properties        SARIFProperties  `json:"properties"` 
11098}
111- 
11299type  SARIFText  struct  {
113100	Text  string  `json:"text"` 
114101}
115- 
116102type  SARIFHelp  struct  {
117103	Text      string  `json:"text"` 
118104	Markdown  string  `json:"markdown"` 
119105}
120- 
121106type  SARIFProperties  struct  {
122107	Precision         string    `json:"precision"` 
123108	SecuritySeverity  string    `json:"security-severity"` 
124109	Tags              []string  `json:"tags"` 
125110}
126- 
127111type  SARIFResult  struct  {
128112	RuleId     string           `json:"ruleId"` 
129113	Level      string           `json:"level"` 
130114	Message    SARIFText        `json:"message"` 
131115	Locations  []SARIFLocation  `json:"locations"` 
132116}
133- 
134117type  SARIFLocation  struct  {
135118	PhysicalLocation  SARIFPhysicalLocation  `json:"physicalLocation"` 
136119	Message           SARIFText              `json:"message"` 
137120}
138- 
139121type  SARIFPhysicalLocation  struct  {
140122	ArtifactLocation  SARIFArtifactLocation  `json:"artifactLocation"` 
141123}
142- 
143124type  SARIFArtifactLocation  struct  {
144125	Uri        string  `json:"uri"` 
145126	UriBaseId  string  `json:"uriBaseId"` 
146127}
147128
148- // ---------- Helpers ---------- 
129+ // Filtering options 
130+ type  FilterOptions  struct  {
131+ 	MinSeverity      string 
132+ 	PackageTypes     []string 
133+ 	NotPackageTypes  []string 
134+ 	ExcludeAccepted  bool 
135+ }
136+ 
137+ // Severity order 
138+ var  severityOrder  =  []string {"Negligible" , "Low" , "Medium" , "High" , "Critical" }
139+ 
140+ func  isSeverityGte (a , b  string ) bool  {
141+ 	var  ai , bi  int  =  - 1 , - 1 
142+ 	for  i , v  :=  range  severityOrder  {
143+ 		if  strings .EqualFold (v , a ) {
144+ 			ai  =  i 
145+ 		}
146+ 		if  strings .EqualFold (v , b ) {
147+ 			bi  =  i 
148+ 		}
149+ 	}
150+ 	return  ai  >=  bi  &&  ai  >=  0  &&  bi  >=  0 
151+ }
152+ 
153+ func  splitAndTrim (s  string ) []string  {
154+ 	parts  :=  strings .Split (s , "," )
155+ 	var  out  []string 
156+ 	for  _ , v  :=  range  parts  {
157+ 		trimmed  :=  strings .TrimSpace (v )
158+ 		if  trimmed  !=  ""  {
159+ 			out  =  append (out , trimmed )
160+ 		}
161+ 	}
162+ 	return  out 
163+ }
164+ 
165+ func  filterPackages (pkgs  []Package , opts  FilterOptions ) []Package  {
166+ 	var  filtered  []Package 
167+ TypeLoop:
168+ 	for  _ , pkg  :=  range  pkgs  {
169+ 		ptype  :=  strings .ToLower (pkg .Type )
170+ 		if  len (opts .PackageTypes ) >  0  {
171+ 			found  :=  false 
172+ 			for  _ , allowed  :=  range  opts .PackageTypes  {
173+ 				if  strings .ToLower (allowed ) ==  ptype  {
174+ 					found  =  true 
175+ 					break 
176+ 				}
177+ 			}
178+ 			if  ! found  {
179+ 				continue  TypeLoop
180+ 			}
181+ 		}
182+ 		for  _ , notAllowed  :=  range  opts .NotPackageTypes  {
183+ 			if  strings .ToLower (notAllowed ) ==  ptype  {
184+ 				continue  TypeLoop
185+ 			}
186+ 		}
187+ 		newVulns  :=  []Vuln {}
188+ 		for  _ , vuln  :=  range  pkg .Vulns  {
189+ 			if  opts .MinSeverity  !=  ""  &&  ! isSeverityGte (vuln .Severity .Value , opts .MinSeverity ) {
190+ 				continue 
191+ 			}
192+ 			if  opts .ExcludeAccepted  &&  len (vuln .AcceptedRisks ) >  0  {
193+ 				fmt .Printf ("Accepted risks: %d\n " , len (vuln .AcceptedRisks ))
194+ 				continue 
195+ 			}
196+ 			newVulns  =  append (newVulns , vuln )
197+ 		}
198+ 		if  len (newVulns ) >  0  {
199+ 			pkg .Vulns  =  newVulns 
200+ 			filtered  =  append (filtered , pkg )
201+ 		}
202+ 	}
203+ 	return  filtered 
204+ }
149205
150- // Converts Sysdig severity string to SARIF level 
151206func  checkLevel (sev  string ) string  {
152207	sev  =  strings .ToLower (sev )
153208	switch  sev  {
@@ -162,59 +217,19 @@ func checkLevel(sev string) string {
162217	}
163218}
164219
165- // Formats a vulnerability's full description for SARIF 
166220func  getVulnFullDescription (pkg  Package , vuln  Vuln ) string  {
167221	return  fmt .Sprintf ("%s\n Severity: %s\n Package: %s\n Type: %s\n Fix: %s\n URL: https://nvd.nist.gov/vuln/detail/%s" ,
168222		vuln .Name , vuln .Severity .Value , pkg .Name , pkg .Type , pkg .SuggestedFix , vuln .Name )
169223}
170224
171- // ---------- Main CLI Entry Point ---------- 
172- 
173- func  main () {
174- 	if  len (os .Args ) <  3  {
175- 		fmt .Println ("Usage: sysdig2sarif input.json output.sarif [groupByPackage]" )
176- 		os .Exit (1 )
177- 	}
178- 	inputFile  :=  os .Args [1 ]
179- 	outputFile  :=  os .Args [2 ]
180- 	groupByPackage  :=  false 
181- 	if  len (os .Args ) >=  4  &&  os .Args [3 ] ==  "true"  {
182- 		groupByPackage  =  true 
183- 	}
184- 
185- 	raw , err  :=  ioutil .ReadFile (inputFile )
186- 	if  err  !=  nil  {
187- 		panic (err )
188- 	}
189- 	var  data  Report 
190- 	if  err  :=  json .Unmarshal (raw , & data ); err  !=  nil  {
191- 		panic (err )
192- 	}
193- 
194- 	sarif  :=  vulnerabilities2SARIF (data , groupByPackage )
195- 	out , err  :=  json .MarshalIndent (sarif , "" , "  " )
196- 	if  err  !=  nil  {
197- 		panic (err )
198- 	}
199- 	if  err  :=  ioutil .WriteFile (outputFile , out , 0644 ); err  !=  nil  {
200- 		panic (err )
201- 	}
202- 	fmt .Println ("SARIF written to" , outputFile )
203- }
204- 
205- // ---------- Conversion Functions ---------- 
206- 
207- // Converts the Sysdig report to a SARIF object 
208225func  vulnerabilities2SARIF (data  Report , groupByPackage  bool ) SARIF  {
209226	var  rules  []SARIFRule 
210227	var  results  []SARIFResult 
211- 
212228	if  groupByPackage  {
213229		rules , results  =  vulnerabilities2SARIFResByPackage (data )
214230	} else  {
215231		rules , results  =  vulnerabilities2SARIFRes (data )
216232	}
217- 
218233	run  :=  SARIFRun {
219234		LogicalLocations : []LogicalLocation {{
220235			Name :               "container-image" ,
@@ -236,11 +251,10 @@ func vulnerabilities2SARIF(data Report, groupByPackage bool) SARIF {
236251			"resultId" :     data .Info .ResultId ,
237252		},
238253	}
239- 	// Fill in tool/driver info 
240254	run .Tool .Driver .Name  =  "sysdig-cli-scanner" 
241255	run .Tool .Driver .FullName  =  "Sysdig Vulnerability CLI Scanner" 
242256	run .Tool .Driver .InformationUri  =  "https://docs.sysdig.com/en/docs/installation/sysdig-secure/install-vulnerability-cli-scanner" 
243- 	run .Tool .Driver .Version  =  "1.0.0"   // Change as appropriate 
257+ 	run .Tool .Driver .Version  =  "1.0.0" 
244258	run .Tool .Driver .SemanticVersion  =  "1.0.0" 
245259	run .Tool .Driver .DottedQuadFileVersion  =  "1.0.0.0" 
246260	run .Tool .Driver .Rules  =  rules 
@@ -252,11 +266,9 @@ func vulnerabilities2SARIF(data Report, groupByPackage bool) SARIF {
252266	}
253267}
254268
255- // SARIF conversion, grouping results by package 
256269func  vulnerabilities2SARIFResByPackage (data  Report ) ([]SARIFRule , []SARIFResult ) {
257270	var  rules  []SARIFRule 
258271	var  results  []SARIFResult 
259- 
260272	for  _ , pkg  :=  range  data .Result .Packages  {
261273		if  len (pkg .Vulns ) ==  0  {
262274			continue 
@@ -309,12 +321,10 @@ func vulnerabilities2SARIFResByPackage(data Report) ([]SARIFRule, []SARIFResult)
309321	return  rules , results 
310322}
311323
312- // SARIF conversion, result per vulnerability 
313324func  vulnerabilities2SARIFRes (data  Report ) ([]SARIFRule , []SARIFResult ) {
314325	var  rules  []SARIFRule 
315326	var  results  []SARIFResult 
316327	seen  :=  map [string ]bool {}
317- 
318328	for  _ , pkg  :=  range  data .Result .Packages  {
319329		for  _ , vuln  :=  range  pkg .Vulns  {
320330			if  ! seen [vuln .Name ] {
@@ -355,3 +365,46 @@ func vulnerabilities2SARIFRes(data Report) ([]SARIFRule, []SARIFResult) {
355365	}
356366	return  rules , results 
357367}
368+ 
369+ func  main () {
370+ 	minSeverity  :=  flag .String ("min-severity" , "" , "Minimum severity (e.g., High)" )
371+ 	packageTypes  :=  flag .String ("type" , "" , "Package types (comma-separated, e.g., java,javascript)" )
372+ 	notPackageTypes  :=  flag .String ("not-type" , "" , "Exclude package types (comma-separated)" )
373+ 	excludeAccepted  :=  flag .Bool ("exclude-accepted" , false , "Exclude vulnerabilities with accepted risks" )
374+ 	groupByPackage  :=  flag .Bool ("group-by-package" , false , "Group by package" )
375+ 	flag .Parse ()
376+ 
377+ 	if  flag .NArg () <  2  {
378+ 		fmt .Println ("Usage: sysdig2sarif [flags] input.json output.sarif" )
379+ 		os .Exit (1 )
380+ 	}
381+ 	inputFile  :=  flag .Arg (0 )
382+ 	outputFile  :=  flag .Arg (1 )
383+ 
384+ 	raw , err  :=  ioutil .ReadFile (inputFile )
385+ 	if  err  !=  nil  {
386+ 		panic (err )
387+ 	}
388+ 	var  data  Report 
389+ 	if  err  :=  json .Unmarshal (raw , & data ); err  !=  nil  {
390+ 		panic (err )
391+ 	}
392+ 
393+ 	opts  :=  FilterOptions {
394+ 		MinSeverity :     * minSeverity ,
395+ 		PackageTypes :    splitAndTrim (* packageTypes ),
396+ 		NotPackageTypes : splitAndTrim (* notPackageTypes ),
397+ 		ExcludeAccepted : * excludeAccepted ,
398+ 	}
399+ 	data .Result .Packages  =  filterPackages (data .Result .Packages , opts )
400+ 
401+ 	sarif  :=  vulnerabilities2SARIF (data , * groupByPackage )
402+ 	out , err  :=  json .MarshalIndent (sarif , "" , "  " )
403+ 	if  err  !=  nil  {
404+ 		panic (err )
405+ 	}
406+ 	if  err  :=  ioutil .WriteFile (outputFile , out , 0644 ); err  !=  nil  {
407+ 		panic (err )
408+ 	}
409+ 	fmt .Println ("SARIF written to" , outputFile )
410+ }
0 commit comments