Skip to content

Commit 1d5d3db

Browse files
committed
merged multiple code risks on same page
2 parents 37689d4 + 5f84797 commit 1d5d3db

20 files changed

Lines changed: 699 additions & 208 deletions

cmd/devguard-cli/commands/migrate.go

Lines changed: 0 additions & 89 deletions
This file was deleted.

cmd/devguard-cli/main.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ func Execute() {
5353

5454
func init() {
5555
rootCmd.AddCommand(commands.NewVulndbCommand())
56-
rootCmd.AddCommand(commands.NewMigrateCommand())
5756
rootCmd.AddCommand(commands.NewComponentsCommand())
5857
rootCmd.AddCommand(commands.NewDaemonCommand())
5958
}

cmd/devguard-scanner/commands/sarif.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"net/http"
2727
"os"
2828
"regexp"
29+
"strconv"
2930
"strings"
3031
"time"
3132

@@ -238,10 +239,11 @@ func obfuscateString(str string) string {
238239
}
239240

240241
// add obfuscation function for snippet
241-
func obfuscateSecret(sarifScan *common.SarifResult) {
242+
func obfuscateSecretAndAddFingerprint(sarifScan *common.SarifResult) {
242243
// obfuscate the snippet
243244
for ru, run := range sarifScan.Runs {
244245
for re, result := range run.Results {
246+
// obfuscate the snippet
245247
for lo, location := range result.Locations {
246248
snippet := location.PhysicalLocation.Region.Snippet.Text
247249
snippetMax := 20
@@ -252,6 +254,10 @@ func obfuscateSecret(sarifScan *common.SarifResult) {
252254
// set the snippet
253255
sarifScan.Runs[ru].Results[re].Locations[lo].PhysicalLocation.Region.Snippet.Text = snippet
254256
}
257+
258+
//set the fingerprint to the calculated fingerprint if it exists
259+
result.Fingerprints.CalculatedFingerprint = result.PartialFingerprints.CommitSha + ":" + result.Locations[0].PhysicalLocation.ArtifactLocation.URI + ":" + result.RuleID + ":" + strconv.Itoa(result.Locations[0].PhysicalLocation.Region.StartLine)
260+
255261
}
256262
}
257263
}

cmd/devguard-scanner/commands/sast.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"os"
99
"os/exec"
1010
"path"
11-
"strconv"
1211

1312
"github.com/jedib0t/go-pretty/v6/table"
1413
"github.com/jedib0t/go-pretty/v6/text"
@@ -26,13 +25,13 @@ func printSastScanResults(firstPartyVulns []vuln.FirstPartyVulnDTO, webUI, asset
2625
green := text.FgGreen
2726
for _, vuln := range firstPartyVulns {
2827
tw.AppendRow(table.Row{"RuleID", vuln.RuleID})
29-
if vuln.Snippet != "" {
30-
tw.AppendRow(table.Row{"Snippet", vuln.Snippet})
28+
for _, snippet := range vuln.SnippetContents {
29+
tw.AppendRow(table.Row{"Snippet", snippet.Snippet})
3130
}
3231
tw.AppendRow(table.Row{"Message", text.WrapText(*vuln.Message, 80)})
3332
if vuln.URI != "" {
34-
tw.AppendRow(table.Row{"File", green.Sprint(vuln.URI + ":" + strconv.Itoa(vuln.StartLine))})
35-
tw.AppendRow(table.Row{"Line", vuln.StartLine})
33+
tw.AppendRow(table.Row{"File", green.Sprint(vuln.URI)})
34+
3635
}
3736
tw.AppendSeparator()
3837
}

cmd/devguard-scanner/commands/secret_scanning.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"os"
99
"os/exec"
1010
"path"
11-
"strconv"
1211

1312
"github.com/jedib0t/go-pretty/v6/table"
1413
"github.com/jedib0t/go-pretty/v6/text"
@@ -75,7 +74,7 @@ func secretScan(p string) (*common.SarifResult, error) {
7574
}
7675

7776
// obfuscate founded secrets
78-
obfuscateSecret(&sarifScan)
77+
obfuscateSecretAndAddFingerprint(&sarifScan)
7978

8079
return &sarifScan, nil
8180
}
@@ -89,16 +88,19 @@ func printSecretScanResults(firstPartyVulns []vuln.FirstPartyVulnDTO, webUI stri
8988
for _, vuln := range firstPartyVulns {
9089
raw := []table.Row{
9190
{"RuleID:", vuln.RuleID},
92-
{"File:", green.Sprint(vuln.URI + ":" + strconv.Itoa(vuln.StartLine))},
93-
{"Snippet:", text.WrapText(vuln.Snippet, 80)},
94-
{"Message:", text.WrapText(*vuln.Message, 80)},
95-
{"Line:", vuln.StartLine},
91+
{"File:", green.Sprint(vuln.URI)},
92+
}
93+
tw.AppendRows(raw)
94+
for _, snippet := range vuln.SnippetContents {
95+
tw.AppendRow(table.Row{"Snippet", snippet.Snippet})
96+
}
97+
raw = []table.Row{{"Message:", text.WrapText(*vuln.Message, 80)},
98+
9699
{"Commit:", vuln.Commit},
97100
{"Author:", vuln.Author},
98101
{"Email:", vuln.Email},
99102
{"Date:", vuln.Date},
100-
{"Link:", blue.Sprint(fmt.Sprintf("%s/%s/refs/%s/code-risks/%s", webUI, assetName, assetVersionName, vuln.ID))},
101-
}
103+
{"Link:", blue.Sprint(fmt.Sprintf("%s/%s/refs/%s/code-risks/%s", webUI, assetName, assetVersionName, vuln.ID))}}
102104

103105
tw.AppendRows(raw)
104106
tw.AppendSeparator()

cmd/devguard-scanner/commands/secret_scanning_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func TestObfuscateSnippet(t *testing.T) {
7373
expectedSnippet := "that is an example c*********************************************************"
7474

7575
// Call the function with the example data
76-
obfuscateSecret(&exampleSarifResult)
76+
obfuscateSecretAndAddFingerprint(&exampleSarifResult)
7777

7878
// Check if the original snippet is not present in the obfuscated result
7979
assert.NotContains(t, exampleSarifResult.Runs[0].Results[0].Locations[0].PhysicalLocation.Region.Snippet.Text, originalSnippet)
@@ -94,7 +94,7 @@ func TestObfuscateSnippet(t *testing.T) {
9494
exampleSarifResult.Runs[0].Results[0].Locations[0].PhysicalLocation.Region.Snippet.Text = originalSnippet
9595

9696
// Call the function with the example data
97-
obfuscateSecret(&exampleSarifResult)
97+
obfuscateSecretAndAddFingerprint(&exampleSarifResult)
9898

9999
// Check if the original snippet is not present in the obfuscated result
100100
assert.NotContains(t, exampleSarifResult.Runs[0].Results[0].Locations[0].PhysicalLocation.Region.Snippet.Text, originalSnippet)

cmd/devguard/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/l3montree-dev/devguard/internal/core"
2727
"github.com/l3montree-dev/devguard/internal/core/daemon"
2828
"github.com/l3montree-dev/devguard/internal/database"
29+
"github.com/l3montree-dev/devguard/internal/database/models"
2930

3031
_ "github.com/lib/pq"
3132
)
@@ -78,6 +79,12 @@ func main() {
7879
slog.Error("failed to run database migrations", "error", err)
7980
panic(errors.New("Failed to run database migrations"))
8081
}
82+
83+
// Run hash migrations if needed (when algorithm version changes)
84+
if err := models.RunHashMigrationsIfNeeded(db); err != nil {
85+
slog.Error("failed to run hash migrations", "error", err)
86+
panic(errors.New("Failed to run hash migrations"))
87+
}
8188
} else {
8289
slog.Info("automatic migrations disabled via AUTO_MIGRATE=false")
8390
}

internal/common/sarif_result.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ type PartialFingerprints struct {
6565
CommitMessage string `json:"commitMessage"`
6666
}
6767
type Fingerprints struct {
68-
MatchBasedID string `json:"matchBasedId/v1"`
68+
MatchBasedID string `json:"matchBasedId/v1"`
69+
CalculatedFingerprint string `json:"calculatedFingerprint/v1"`
6970
}
7071

7172
type Properties struct {

internal/core/assetversion/asset_version_service.go

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func preferMarkdown(text common.Text) string {
9797

9898
func (s *service) HandleFirstPartyVulnResult(org models.Org, project models.Project, asset models.Asset, assetVersion *models.AssetVersion, sarifScan common.SarifResult, scannerID string, userID string) (int, int, []models.FirstPartyVuln, error) {
9999

100-
firstPartyVulnerabilities := []models.FirstPartyVuln{}
100+
firstPartyVulnerabilitiesMap := make(map[string]models.FirstPartyVuln)
101101

102102
ruleMap := make(map[string]common.Rule)
103103
for _, run := range sarifScan.Runs {
@@ -135,22 +135,58 @@ func (s *service) HandleFirstPartyVulnResult(org models.Org, project models.Proj
135135
firstPartyVulnerability.Date = result.PartialFingerprints.Date
136136
}
137137

138+
var hash string
139+
if result.Fingerprints != nil {
140+
if result.Fingerprints.CalculatedFingerprint != "" {
141+
firstPartyVulnerability.Fingerprint = result.Fingerprints.CalculatedFingerprint
142+
}
143+
}
144+
138145
if len(result.Locations) > 0 {
139146
firstPartyVulnerability.URI = result.Locations[0].PhysicalLocation.ArtifactLocation.URI
140-
firstPartyVulnerability.StartLine = result.Locations[0].PhysicalLocation.Region.StartLine
141-
firstPartyVulnerability.StartColumn = result.Locations[0].PhysicalLocation.Region.StartColumn
142-
firstPartyVulnerability.EndLine = result.Locations[0].PhysicalLocation.Region.EndLine
143-
firstPartyVulnerability.EndColumn = result.Locations[0].PhysicalLocation.Region.EndColumn
144-
firstPartyVulnerability.Snippet = result.Locations[0].PhysicalLocation.Region.Snippet.Text
147+
148+
snippetContent := models.SnippetContent{
149+
StartLine: result.Locations[0].PhysicalLocation.Region.StartLine,
150+
EndLine: result.Locations[0].PhysicalLocation.Region.EndLine,
151+
StartColumn: result.Locations[0].PhysicalLocation.Region.StartColumn,
152+
EndColumn: result.Locations[0].PhysicalLocation.Region.EndColumn,
153+
Snippet: result.Locations[0].PhysicalLocation.Region.Snippet.Text,
154+
}
155+
156+
hash = firstPartyVulnerability.CalculateHash()
157+
if existingVuln, ok := firstPartyVulnerabilitiesMap[hash]; ok {
158+
snippetContents, err := existingVuln.FromJSONSnippetContents()
159+
if err != nil {
160+
return 0, 0, []models.FirstPartyVuln{}, errors.Wrap(err, "could not parse existing snippet contents")
161+
}
162+
snippetContents.Snippets = append(snippetContents.Snippets, snippetContent)
163+
firstPartyVulnerability.SnippetContents, err = snippetContents.ToJSON()
164+
if err != nil {
165+
return 0, 0, []models.FirstPartyVuln{}, errors.Wrap(err, "could not convert snippet contents to JSON")
166+
}
167+
168+
} else {
169+
170+
snippetContents := models.SnippetContents{
171+
Snippets: []models.SnippetContent{snippetContent},
172+
}
173+
var err error
174+
firstPartyVulnerability.SnippetContents, err = snippetContents.ToJSON()
175+
if err != nil {
176+
return 0, 0, []models.FirstPartyVuln{}, errors.Wrap(err, "could not convert snippet contents to JSON")
177+
}
178+
179+
}
180+
firstPartyVulnerabilitiesMap[hash] = firstPartyVulnerability
181+
145182
}
146183

147-
firstPartyVulnerabilities = append(firstPartyVulnerabilities, firstPartyVulnerability)
148184
}
149185
}
150-
151-
firstPartyVulnerabilities = utils.UniqBy(firstPartyVulnerabilities, func(f models.FirstPartyVuln) string {
152-
return f.CalculateHash()
153-
})
186+
var firstPartyVulnerabilities []models.FirstPartyVuln
187+
for _, vuln := range firstPartyVulnerabilitiesMap {
188+
firstPartyVulnerabilities = append(firstPartyVulnerabilities, vuln)
189+
}
154190

155191
amountOpened, amountClosed, amountExisting, err := s.handleFirstPartyVulnResult(userID, scannerID, assetVersion, firstPartyVulnerabilities, asset, org, project)
156192
if err != nil {
@@ -186,18 +222,42 @@ func (s *service) handleFirstPartyVulnResult(userID string, scannerID string, as
186222
fixedVulns := comparison.OnlyInA
187223
newVulns := comparison.OnlyInB
188224

225+
inBoth := comparison.InBoth // these are the vulns that are already in the database, but we need to update them
226+
227+
updatedFirstPartyVulns := make([]models.FirstPartyVuln, 0)
228+
229+
for i := range inBoth {
230+
for n := range vulns {
231+
if inBoth[i].ID == vulns[n].ID {
232+
// we found a new vuln that is already in the database, we need to update it
233+
inBoth[i].SnippetContents = vulns[n].SnippetContents
234+
updatedFirstPartyVulns = append(updatedFirstPartyVulns, inBoth[i])
235+
}
236+
}
237+
}
238+
189239
// get a transaction
190240
if err := s.firstPartyVulnRepository.Transaction(func(tx core.DB) error {
191241
if err := s.firstPartyVulnService.UserDetectedFirstPartyVulns(tx, userID, scannerID, newVulns); err != nil {
192242
// this will cancel the transaction
193243
return err
194244
}
195-
return s.firstPartyVulnService.UserFixedFirstPartyVulns(tx, userID, fixedVulns)
245+
if err := s.firstPartyVulnService.UserFixedFirstPartyVulns(tx, userID, fixedVulns); err != nil {
246+
return err
247+
}
248+
249+
// update existing first party vulns within the transaction
250+
for _, v := range updatedFirstPartyVulns {
251+
if err := s.firstPartyVulnRepository.Save(tx, &v); err != nil {
252+
slog.Error("could not update existing first party vulns", "err", err)
253+
return err
254+
}
255+
}
256+
return nil
196257
}); err != nil {
197258
slog.Error("could not save vulns", "err", err)
198259
return 0, 0, []models.FirstPartyVuln{}, err
199260
}
200-
201261
// the amount we actually fixed, is the amount that was open before
202262
fixedVulns = utils.Filter(fixedVulns, func(vuln models.FirstPartyVuln) bool {
203263
return vuln.State == models.VulnStateOpen
@@ -217,7 +277,7 @@ func (s *service) handleFirstPartyVulnResult(userID string, scannerID string, as
217277
}()
218278
}
219279

220-
return len(newVulns), len(fixedVulns), append(newVulns, comparison.InBoth...), nil
280+
return len(newVulns), len(fixedVulns), append(newVulns, inBoth...), nil
221281
}
222282

223283
func (s *service) HandleScanResult(org models.Org, project models.Project, asset models.Asset, assetVersion *models.AssetVersion, vulns []models.VulnInPackage, scannerID string, userID string) (opened []models.DependencyVuln, closed []models.DependencyVuln, newState []models.DependencyVuln, err error) {

0 commit comments

Comments
 (0)