Skip to content

Commit 12451c9

Browse files
committed
refactor code and do latetst check first
1 parent 24c38e4 commit 12451c9

1 file changed

Lines changed: 144 additions & 176 deletions

File tree

cmd/devguard-cli/test/quickfix.go

Lines changed: 144 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"io"
99
"net/http"
10+
"regexp"
1011
"sort"
1112
"strings"
1213
)
@@ -39,38 +40,25 @@ func getVersion(packageManager string, pkg RegistryRequest) (*http.Response, err
3940
return nil, nil
4041
}
4142

42-
func generalizeAllVersions(resp []byte) [][]string {
43-
var npmResponseObject NPMResponse
44-
45-
err := json.Unmarshal(resp, &npmResponseObject)
46-
47-
if err != nil {
48-
fmt.Println("Error unmarshalling JSON:", err)
49-
return nil
50-
}
51-
43+
func getRecommendedVersions(npmResponse NPMResponse, currentVersion string) ([]string, error) {
5244
var versions [][]string
53-
for _, Obj := range npmResponseObject.Versions {
54-
// skip release candidates since recommending alpha version is not a good idea for security updates haha
55-
if strings.Contains(Obj.Version, "-") {
45+
46+
// Extract and filter versions from NPMResponse
47+
for _, obj := range npmResponse.Versions {
48+
// skip release candidates
49+
if strings.Contains(obj.Version, "-") {
5650
continue
5751
}
58-
// split numbers into array to easily compare major versions later
59-
versionParts := strings.Split(Obj.Version, ".")
52+
versionParts := strings.Split(obj.Version, ".")
6053
versions = append(versions, versionParts)
61-
6254
}
63-
return versions
64-
}
6555

66-
func filterMajorVersions(versionHistory [][]string, currentVersion string) ([]string, error) {
67-
// currentParts := strings.Split(currentVersion, ".")
56+
// Filter by major version and sort
6857
var currentMajor, currentMinor, currentPatch int
6958
fmt.Sscanf(currentVersion, "%d.%d.%d", &currentMajor, &currentMinor, &currentPatch)
7059

7160
var recommended []string
72-
73-
for _, version := range versionHistory {
61+
for _, version := range versions {
7462
var major, minor, patch int
7563
versionStr := strings.Join(version, ".")
7664
fmt.Sscanf(versionStr, "%d.%d.%d", &major, &minor, &patch)
@@ -113,112 +101,32 @@ type DependencyNode struct {
113101
Dependencies map[string]*DependencyNode
114102
}
115103

116-
func caretHandler(version []string) string {
117-
//version range detection:
118-
// example
119-
/*
120-
| Dependency | Actual Range |
121-
| ---------------------------------------|---------------- |
122-
| socks-proxy-agent@^7.0.0 | >=7.0.0 <8.0.0 |
123-
| debug@^4.3.3 | >=4.3.3 <5.0.0 |
124-
| ip@^1.1.5 |>=1.1.5 <2.0.0 |
125-
| test@^0.2.3 | >= 0.2.3 < 0.3.0|
126-
caret applies to the most left non-zero digit in the version
127-
Range detection:
128-
129-
130-
*/
131-
//todo detect minRange maxRange
132-
// minRange := "7.0.0"
133-
// maxRange := "8.0.0"
134-
135-
// for _, version := range versionHistory {
136-
// if version[0] == currentParts[0] {
137-
// if version[1] >= currentParts[1] {
138-
// if version[2] >= currentParts[2] {
139-
// // fmt.Println(strings.Join(version, "."))
140-
// recommended = append(recommended, strings.Join(version, "."))
141-
// }
142-
// }
143-
// }
144-
// return ""
145-
// }
146-
return ""
147-
}
148-
func suggestVersion(currentVersion string, vulnerableVersion string) string {
149-
// alright so
150-
return ""
151-
}
152-
153-
func walkDependencyTree(npmRegisterResp []byte, depName string, depVersion string, visited map[string]bool, vulnerablePackage string, vulnerableVersion string) *DependencyNode {
154-
var jsonData NPMResponse
104+
func IsValidSemver(version string) bool {
155105

156-
if err := json.Unmarshal(npmRegisterResp, &jsonData); err != nil {
157-
// fmt.Println("Error unmarshalling JSON:", err)
158-
return nil
159-
}
160-
161-
nodeKey := depName + "@" + depVersion
162-
if visited[nodeKey] {
163-
return nil
164-
}
165-
visited[nodeKey] = true
166-
167-
node := &DependencyNode{
168-
Name: depName,
169-
Version: depVersion,
170-
Dependencies: make(map[string]*DependencyNode),
171-
}
172-
173-
if jsonData.Dependencies == nil && jsonData.DevDependencies == nil && jsonData.PeerDependencies == nil && jsonData.OptionalDependencies == nil {
174-
//fmt.Printf("No dependencies found for %s@%s\n", depName, depVersion)
175-
return node
176-
}
177-
178-
for depKey, depVal := range jsonData.Dependencies {
179-
//fmt.Printf("Fetching dependency: %s@%s\n", depKey, depVal)
180-
181-
// Check if version exists before fetching
182-
if !VersionExists(depKey, depVal) {
183-
fmt.Printf("Skipping %s@%s: version not found\n", depKey, depVal)
184-
continue
185-
}
106+
pattern := `^\d+\.\d+\.\d+$`
107+
matched, _ := regexp.MatchString(pattern, version)
108+
return matched
109+
}
186110

187-
depResp, err := GetNPMRegistry(RegistryRequest{Dependency: depKey, Version: depVal})
188-
if err != nil {
189-
fmt.Printf("Error fetching %s@%s: %v\n", depKey, depVal, err)
190-
continue
191-
}
111+
func processDependencies(depMap map[string]string, depName string, depVersion string, visited map[string]bool, vulnerablePackage string, vulnerableVersion string, node *DependencyNode) {
112+
for depKey, depVal := range depMap {
113+
// remove ^, ~, quotes
114+
normalizedDepVal := strings.Trim(depVal, "^~\"")
192115

193-
depBody, err := io.ReadAll(depResp.Body)
194-
depResp.Body.Close()
195-
if err != nil {
196-
fmt.Printf("Error reading response for %s@%s: %v\n", depKey, depVal, err)
116+
// Skip non-semver versions
117+
if !IsValidSemver(normalizedDepVal) {
197118
continue
198119
}
199120

200-
// Recursive call
201-
childNode := walkDependencyTree(depBody, depKey, depVal, visited, vulnerablePackage, vulnerableVersion)
202-
if childNode != nil {
203-
node.Dependencies[depKey] = childNode
204-
}
205-
if depKey == vulnerablePackage && depVal == vulnerableVersion {
206-
fmt.Printf("Vulnerable package found: %s@%s\n", depKey, depVal)
207-
}
208-
}
209-
210-
for depKey, depVal := range jsonData.OptionalDependencies {
211-
//fmt.Printf("Fetching optional dependency: %s@%s\n", depKey, depVal)
212-
213121
// Check if version exists before fetching
214-
if !VersionExists(depKey, depVal) {
215-
fmt.Printf("Skipping %s@%s: version not found\n", depKey, depVal)
122+
if !VersionExists(depKey, normalizedDepVal) {
123+
fmt.Printf("Skipping %s@%s: version not found\n", depKey, normalizedDepVal)
216124
continue
217125
}
218126

219127
depResp, err := GetNPMRegistry(RegistryRequest{Dependency: depKey, Version: depVal})
220128
if err != nil {
221-
fmt.Printf("Error fetching %s@%s: %v\n", depKey, node.Version, err)
129+
fmt.Printf("Error fetching %s@%s: %v\n", depKey, depVal, err)
222130
continue
223131
}
224132

@@ -238,39 +146,36 @@ func walkDependencyTree(npmRegisterResp []byte, depName string, depVersion strin
238146
fmt.Printf("Vulnerable package found: %s@%s\n", depKey, depVal)
239147
}
240148
}
149+
}
241150

242-
for depKey, depVal := range jsonData.DevDependencies {
243-
//fmt.Printf("Fetching dev dependency: %s@%s\n", depKey, depVal)
151+
func walkDependencyTree(npmRegisterResp []byte, depName string, depVersion string, visited map[string]bool, vulnerablePackage string, vulnerableVersion string) *DependencyNode {
152+
var jsonData NPMResponse
244153

245-
// Check if version exists before fetching
246-
if !VersionExists(depKey, depVal) {
247-
fmt.Printf("Skipping %s@%s: version not found\n", depKey, depVal)
248-
continue
249-
}
154+
if err := json.Unmarshal(npmRegisterResp, &jsonData); err != nil {
155+
return nil
156+
}
250157

251-
depResp, err := GetNPMRegistry(RegistryRequest{Dependency: depKey, Version: depVal})
252-
if err != nil {
253-
fmt.Printf("Error fetching %s@%s: %v\n", depKey, depVal, err)
254-
continue
255-
}
158+
nodeKey := depName + "@" + depVersion
159+
if visited[nodeKey] {
160+
return nil
161+
}
162+
visited[nodeKey] = true
256163

257-
depBody, err := io.ReadAll(depResp.Body)
258-
depResp.Body.Close()
259-
if err != nil {
260-
fmt.Printf("Error reading response for %s@%s: %v\n", depKey, depVal, err)
261-
continue
262-
}
164+
node := &DependencyNode{
165+
Name: depName,
166+
Version: depVersion,
167+
Dependencies: make(map[string]*DependencyNode),
168+
}
263169

264-
// Recursive call
265-
childNode := walkDependencyTree(depBody, depKey, depVal, visited, vulnerablePackage, vulnerableVersion)
266-
if childNode != nil {
267-
node.Dependencies[depKey] = childNode
268-
}
269-
if depKey == vulnerablePackage && depVal == vulnerableVersion {
270-
fmt.Printf("Vulnerable package found: %s@%s\n", depKey, depVal)
271-
}
170+
if jsonData.Dependencies == nil && jsonData.DevDependencies == nil && jsonData.PeerDependencies == nil && jsonData.OptionalDependencies == nil {
171+
return node
272172
}
273173

174+
// Process all dependency types using the same logic
175+
processDependencies(jsonData.Dependencies, depName, depVersion, visited, vulnerablePackage, vulnerableVersion, node)
176+
processDependencies(jsonData.OptionalDependencies, depName, depVersion, visited, vulnerablePackage, vulnerableVersion, node)
177+
processDependencies(jsonData.DevDependencies, depName, depVersion, visited, vulnerablePackage, vulnerableVersion, node)
178+
274179
return node
275180
}
276181

@@ -286,51 +191,114 @@ func printDependencyTree(node *DependencyNode, indent string) {
286191
}
287192
}
288193

289-
func main() {
290-
DirectDependency := "playwright"
291-
currentVersion := "1.50.1"
292-
vulnerablePackage := "ip"
293-
vulnerableVersion := "1.1.5"
294-
resp, err := getVersion(getPackageManager("npm"), RegistryRequest{Dependency: DirectDependency})
194+
func findDependencyVersion(npmResp NPMResponse, depName string) string {
195+
// Check all dependency types
196+
if version, ok := npmResp.Dependencies[depName]; ok {
197+
return version
198+
}
199+
if version, ok := npmResp.OptionalDependencies[depName]; ok {
200+
return version
201+
}
202+
if version, ok := npmResp.DevDependencies[depName]; ok {
203+
return version
204+
}
205+
if version, ok := npmResp.PeerDependencies[depName]; ok {
206+
return version
207+
}
208+
return ""
209+
}
210+
211+
func fetchPackageMetadata(pkgManager string, dep string, version string) (*NPMResponse, error) {
212+
resp, err := getVersion(pkgManager, RegistryRequest{Dependency: dep, Version: version})
295213
if err != nil {
296-
fmt.Println("Error:", err)
297-
return
214+
return nil, fmt.Errorf("error fetching %s@%s: %w", dep, version, err)
298215
}
216+
299217
body, err := io.ReadAll(resp.Body)
218+
resp.Body.Close()
300219
if err != nil {
301-
fmt.Println("Error reading body:", err)
302-
return
220+
return nil, fmt.Errorf("error reading response for %s@%s: %w", dep, version, err)
221+
}
222+
223+
var npmResp NPMResponse
224+
if err := json.Unmarshal(body, &npmResp); err != nil {
225+
return nil, fmt.Errorf("error unmarshalling JSON for %s@%s: %w", dep, version, err)
303226
}
304-
defer resp.Body.Close()
305227

306-
versions, err := filterMajorVersions(generalizeAllVersions(body), currentVersion)
228+
return &npmResp, nil
229+
}
230+
231+
func checkVersionAvailability(versions []string, currentVersion string) (string, error) {
232+
if len(versions) == 0 {
233+
return "", fmt.Errorf("no versions available")
234+
}
235+
236+
if versions[0] == currentVersion {
237+
return "", fmt.Errorf("no new version available (current: %s)", currentVersion)
238+
}
239+
240+
return versions[0], nil
241+
}
242+
243+
func checkVulnerabilityStatus(latestMeta *NPMResponse, vulnPkg string, vulnVer string) (bool, string) {
244+
latestVulnVer := findDependencyVersion(*latestMeta, vulnPkg)
245+
246+
if latestVulnVer == vulnVer {
247+
return false, latestVulnVer
248+
}
249+
250+
if latestVulnVer != "" && latestVulnVer != vulnVer {
251+
return true, latestVulnVer
252+
}
253+
254+
return false, latestVulnVer
255+
}
256+
257+
func checkVulnerabilityFix(directDep string, currentVer string, vulnPkg string, vulnVer string) error {
258+
259+
npmMeta, err := fetchPackageMetadata(getPackageManager("npm"), directDep, "")
307260
if err != nil {
308-
fmt.Println("Error filtering versions:", err)
309-
return
261+
return fmt.Errorf("failed to fetch package metadata: %w", err)
310262
}
311263

312-
for _, version := range versions {
313-
npmResponse, err := GetNPMRegistry(RegistryRequest{Dependency: DirectDependency, Version: version})
314-
if err != nil {
315-
fmt.Println("Error fetching version details:", err)
316-
continue
317-
}
264+
versions, err := getRecommendedVersions(*npmMeta, currentVer)
265+
if err != nil {
266+
return fmt.Errorf("failed to filter versions: %w", err)
267+
}
318268

319-
response, err := io.ReadAll(npmResponse.Body)
320-
npmResponse.Body.Close()
321-
if err != nil {
322-
fmt.Println("Error reading response:", err)
323-
continue
324-
}
269+
latestVer, err := checkVersionAvailability(versions, currentVer)
270+
if err != nil {
271+
fmt.Println(err)
272+
return err
273+
}
325274

326-
// Build dependency tree recursively
327-
visited := make(map[string]bool)
328-
tree := walkDependencyTree(response, DirectDependency, version, visited, vulnerablePackage, vulnerableVersion)
275+
fmt.Printf("New versions available for %s: %s -> %s\n", directDep, currentVer, latestVer)
329276

330-
// fmt.Println(tree)
331-
for _, dep := range tree.Dependencies {
332-
fmt.Println(dep)
333-
}
334-
printDependencyTree(tree, "")
277+
latestMeta, err := fetchPackageMetadata(getPackageManager("npm"), directDep, "latest")
278+
if err != nil {
279+
return fmt.Errorf("failed to fetch latest version metadata: %w", err)
280+
}
281+
282+
isFixed, newVer := checkVulnerabilityStatus(latestMeta, vulnPkg, vulnVer)
283+
284+
if !isFixed {
285+
fmt.Printf("Vulnerability NOT fixed in latest %s (still uses %s@%s)\n", directDep, vulnPkg, vulnVer)
286+
return nil
287+
}
288+
289+
fmt.Printf("✓ Vulnerability FIXED in latest (uses %s@%s instead of %s)\n", vulnPkg, newVer, vulnVer)
290+
return nil
291+
}
292+
293+
func main() {
294+
directDependency := "playwright"
295+
currentVersion := "1.50.1"
296+
directVulnerablePackage := "fsevents"
297+
directVulnerableVersion := "2.3.2"
298+
// transitiveVulnerablePackage := "ip"
299+
// transitiveVulnerableVersion := "1.1.5"
300+
301+
if err := checkVulnerabilityFix(directDependency, currentVersion, directVulnerablePackage, directVulnerableVersion); err != nil {
302+
fmt.Println("Error:", err)
335303
}
336304
}

0 commit comments

Comments
 (0)