By using this site, you agree to the Privacy Policy and Terms of Use.
Accept
World of SoftwareWorld of SoftwareWorld of Software
  • News
  • Software
  • Mobile
  • Computing
  • Gaming
  • Videos
  • More
    • Gadget
    • Web Stories
    • Trending
    • Press Release
Search
  • Privacy
  • Terms
  • Advertise
  • Contact
Copyright © All Rights Reserved. World of Software.
Reading: Building a Go Dependency Scanner From Scratch | HackerNoon
Share
Sign In
Notification Show More
Font ResizerAa
World of SoftwareWorld of Software
Font ResizerAa
  • Software
  • Mobile
  • Computing
  • Gadget
  • Gaming
  • Videos
Search
  • News
  • Software
  • Mobile
  • Computing
  • Gaming
  • Videos
  • More
    • Gadget
    • Web Stories
    • Trending
    • Press Release
Have an existing account? Sign In
Follow US
  • Privacy
  • Terms
  • Advertise
  • Contact
Copyright © All Rights Reserved. World of Software.
World of Software > Computing > Building a Go Dependency Scanner From Scratch | HackerNoon
Computing

Building a Go Dependency Scanner From Scratch | HackerNoon

News Room
Last updated: 2025/08/19 at 9:25 AM
News Room Published 19 August 2025
Share
SHARE

When managing Go projects, you need to track dependencies, check for vulnerabilities, and ensure license compliance. Instead of relying on external tools, let’s build our own dependency analyzer using Go’s standard library.

The Core Structure

We’ll work with Go modules, so we need structures to represent them:

package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"regexp"
	"sort"
	"strings"
	"time"
)

type Module struct {
	Path     string
	Version  string
	Indirect bool
}

type GoMod struct {
	Module   Module
	Requires []Module
}

Our tool will handle three operations: listing dependencies, vulnerability scanning, and license checking.

Parsing go.mod Files

Understanding Module File Structure

The go.mod file uses a specific format that we need to parse correctly. Module declarations start with the module keyword followed by the module path. Dependencies are listed in require statements, which can be single-line or grouped in multi-line blocks.

The parsing logic handles both formats by tracking whether we’re inside a multi-line require block. We use regular expressions to extract the module path and version from each line, and detect indirect dependencies by looking for the // indirect comment. This approach gives us the same information that go list would provide, but without spawning external processes.

Rather than shelling out to go list, we can parse the go.mod file directly:

func parseGoMod() (*GoMod, error) {
	file, err := os.Open("go.mod")
	if err != nil {
		return nil, fmt.Errorf("go.mod not found: %v", err)
	}
	defer file.Close()

	goMod := &GoMod{
		Requires: []Module{},
	}

	scanner := bufio.NewScanner(file)
	inRequire := false
	requireRegex := regexp.MustCompile(`^s*([^s]+)s+([^s]+)(?:s+//s*indirect)?`)
	moduleRegex := regexp.MustCompile(`^modules+(.+)`)

	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())

		if strings.HasPrefix(line, "module ") {
			if matches := moduleRegex.FindStringSubmatch(line); len(matches) > 1 {
				goMod.Module = Module{Path: matches[1]}
			}
		}

		if strings.HasPrefix(line, "require (") {
			inRequire = true
			continue
		}

		if inRequire && line == ")" {
			inRequire = false
			continue
		}

		if inRequire || strings.HasPrefix(line, "require ") {
			cleanLine := strings.TrimPrefix(line, "require ")
			if matches := requireRegex.FindStringSubmatch(cleanLine); len(matches) >= 3 {
				module := Module{
					Path:     matches[1],
					Version:  matches[2],
					Indirect: strings.Contains(line, "indirect"),
				}
				goMod.Requires = append(goMod.Requires, module)
			}
		}
	}

	return goMod, scanner.Err()
}

The parser handles both single-line requires and multi-line require blocks. It extracts module paths, versions, and identifies indirect dependencies.

Vulnerability Database Queries

How Vulnerability Checking Works

Vulnerability databases maintain records of known security issues in software packages. Each vulnerability gets assigned identifiers like CVE numbers and includes details about affected versions. The process works like this: we send the package name and version to the database API, it checks if that specific version has any known vulnerabilities, then returns a list of issues if found.

The OSV database is particularly useful because it aggregates vulnerability data from multiple sources and provides a unified API. When we query it, we’re essentially asking “does this exact version of this package have any reported security problems?” The database performs version matching and returns structured data about any findings.

We can check the OSV (Open Source Vulnerabilities) database for known issues:

func checkOSVDatabase(modulePath, version string) []string {
	url := "https://api.osv.dev/v1/query"

	payload := map[string]interface{}{
		"package": map[string]string{
			"name":      modulePath,
			"ecosystem": "Go",
		},
		"version": version,
	}

	jsonData, err := json.Marshal(payload)
	if err != nil {
		return []string{}
	}

	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Post(url, "application/json", strings.NewReader(string(jsonData)))
	if err != nil {
		return []string{}
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return []string{}
	}

	var result struct {
		Vulns []struct {
			ID      string `json:"id"`
			Summary string `json:"summary"`
		} `json:"vulns"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return []string{}
	}

	var vulnerabilities []string
	for _, vuln := range result.Vulns {
		vulnStr := fmt.Sprintf("%s: %s", vuln.ID, vuln.Summary)
		vulnerabilities = append(vulnerabilities, vulnStr)
	}

	return vulnerabilities
}

func checkVulnerabilities() {
	goMod, err := parseGoMod()
	if err != nil {
		fmt.Printf("Error: %vn", err)
		return
	}

	vulnerableModules := 0

	for i, mod := range goMod.Requires {
		fmt.Printf("rScanning %d/%d: %s", i+1, len(goMod.Requires), mod.Path)
		vulns := checkOSVDatabase(mod.Path, mod.Version)
		if len(vulns) > 0 {
			vulnerableModules++
			fmt.Printf("n🚨 %s@%s:n", mod.Path, mod.Version)
			for _, vuln := range vulns {
				fmt.Printf("  - %sn", vuln)
			}
		}
	}

	if vulnerableModules == 0 {
		fmt.Println("n✅ No known vulnerabilities found")
	} else {
		fmt.Printf("n⚠️ Found %d vulnerable modulesn", vulnerableModules)
	}
}

The vulnerability checker sends a JSON payload with the module name and version, then parses the response for any reported vulnerabilities.

License Information Fetching

How License Detection Works

License compliance checking involves identifying what legal terms govern each dependency in your project. Most open source projects include license files in their repositories, and platforms like GitHub parse these files to identify the license type using SPDX identifiers.

Our approach leverages GitHub’s license detection API, which analyzes repository contents and returns standardized license identifiers. For modules hosted on GitHub, we extract the owner and repository name from the module path, then query GitHub’s API endpoint that specifically provides license information. This gives us machine-readable license data without having to download and parse license files ourselves.

Different licenses have different requirements , some like MIT are very permissive, while others like GPL have copyleft requirements that might affect how you can distribute your software. Understanding these differences is crucial for legal compliance.

For GitHub-hosted modules, we can get license data from their API:

func fetchGitHubLicense(owner, repo string) string {
	url := fmt.Sprintf("https://api.github.com/repos/%s/%s/license", owner, repo)

	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Get(url)
	if err != nil {
		return "Unknown"
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return "Unknown"
	}

	var result struct {
		License struct {
			SPDXID string `json:"spdx_id"`
		} `json:"license"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return "Unknown"
	}

	if result.License.SPDXID != "" && result.License.SPDXID != "NOASSERTION" {
		return result.License.SPDXID
	}

	return "Unknown"
}

func fetchLicenseFromRepo(modulePath string) string {
	if strings.HasPrefix(modulePath, "golang.org/x/") {
		return "BSD-3-Clause"
	}

	if !strings.HasPrefix(modulePath, "github.com/") {
		return "Unknown"
	}

	parts := strings.Split(modulePath, "/")
	if len(parts) < 3 {
		return "Unknown"
	}

	return fetchGitHubLicense(parts[1], parts[2])
}

func checkLicenses() {
	goMod, err := parseGoMod()
	if err != nil {
		fmt.Printf("Error: %vn", err)
		return
	}

	licenseCount := make(map[string]int)

	for i, mod := range goMod.Requires {
		fmt.Printf("rProcessing %d/%d...", i+1, len(goMod.Requires))
		license := fetchLicenseFromRepo(mod.Path)
		licenseCount[license]++
		fmt.Printf("r  %s: %sn", mod.Path, license)
	}

	fmt.Println("nLicense Distribution:")
	for license, count := range licenseCount {
		percentage := float64(count) / float64(len(goMod.Requires)) * 100
		fmt.Printf("  %s: %d modules (%.1f%%)n", license, count, percentage)
	}
}

The license checker recognizes that golang.org/x/ packages use BSD-3-Clause, then queries GitHub’s API for other repositories.

Dependency Analysis with Checksum Verification

The dependency analyzer lists modules and verifies their integrity using go.sum:

func parseGoSum() map[string]string {
	checksums := make(map[string]string)

	file, err := os.Open("go.sum")
	if err != nil {
		return checksums
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		parts := strings.Fields(scanner.Text())
		if len(parts) >= 3 {
			module := parts[0] + "@" + parts[1]
			checksums[module] = parts[2]
		}
	}

	return checksums
}

func analyzeDependencies() {
	goMod, err := parseGoMod()
	if err != nil {
		fmt.Printf("Error: %vn", err)
		return
	}

	checksums := parseGoSum()

	fmt.Printf("Module: %sn", goMod.Module.Path)
	fmt.Printf("Found %d dependencies:nn", len(goMod.Requires))

	direct, indirect := 0, 0

	sort.Slice(goMod.Requires, func(i, j int) bool {
		return goMod.Requires[i].Path < goMod.Requires[j].Path
	})

	for _, mod := range goMod.Requires {
		status := "direct"
		if mod.Indirect {
			status = "indirect"
			indirect++
		} else {
			direct++
		}

		checksumKey := mod.Path + "@" + mod.Version
		hasChecksum := "❌"
		if _, exists := checksums[checksumKey]; exists {
			hasChecksum = "✅"
		}

		fmt.Printf("  %s %s@%s (%s)n", hasChecksum, mod.Path, mod.Version, status)
	}

	fmt.Printf("nSummary: %d direct, %d indirect dependenciesn", direct, indirect)
}

Command Interface

The main function routes commands to the appropriate handlers:

func main() {
	if len(os.Args) < 2 {
		fmt.Println("Usage: go run main.go <command>")
		fmt.Println("Commands:")
		fmt.Println("  deps      List all dependencies")
		fmt.Println("  vulns     Check for vulnerabilities")
		fmt.Println("  licenses  Check license compliance")
		os.Exit(1)
	}

	switch os.Args[1] {
	case "deps":
		analyzeDependencies()
	case "vulns":
		checkVulnerabilities()
	case "licenses":
		checkLicenses()
	default:
		fmt.Println("Unknown command")
		os.Exit(1)
	}
}

Save the code as main.go and run it in any Go project:

# List dependencies with checksum verification
go run main.go deps

# Scan for vulnerabilities
go run main.go vulns

# Analyze licenses
go run main.go licenses

The output shows dependency information, vulnerability reports, and license distribution across your project’s dependencies. The tool demonstrates how dependency analysis works behind the scenes, parsing module files, querying public APIs, and cross-referencing data sources.

This implementation covers the basic concepts but is just a starting point. Real vulnerability scanning requires comprehensive databases, sophisticated version range matching, false positive filtering, and robust error handling. License compliance tools need legal policy engines, compatibility matrices, and custom license detection beyond what GitHub’s API provides. For production use, you’d want multiple data sources, caching, rate limiting, and much more thorough validation logic.

Happy coding 😉

You can find source code here https://github.com/rezmoss/go-dependency-scanner

Sign Up For Daily Newsletter

Be keep up! Get the latest breaking news delivered straight to your inbox.
By signing up, you agree to our Terms of Use and acknowledge the data practices in our Privacy Policy. You may unsubscribe at any time.
Share This Article
Facebook Twitter Email Print
Share
What do you think?
Love0
Sad0
Happy0
Sleepy0
Angry0
Dead0
Wink0
Previous Article The AI-Powered PDF Marks the End of an Era
Next Article PlayStation Plus Subscribers Can Play Marvel's Spider-Man and More Now
Leave a comment

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Stay Connected

248.1k Like
69.1k Follow
134k Pin
54.3k Follow

Latest News

Apple is reportedly making more of its new iPhones in India instead of China
News
How to Use On-Screen Visual Intelligence on iPhone (iOS 26)
News
ECUre: The AI-Powered Guardian Securing Your Car’s Electronic Control Units from Malware | HackerNoon
Computing
Labubus Are on Track to Be a Billion-Dollar Business This Year
Gadget

You Might also Like

Computing

ECUre: The AI-Powered Guardian Securing Your Car’s Electronic Control Units from Malware | HackerNoon

84 Min Read
Computing

Excel formula meets AI prompt: Microsoft brings new ‘COPILOT’ function to spreadsheet cells

2 Min Read
Computing

Pinned Device Memory Patches For Intel’s Multi-GPU “Project Battlematrix” Linux Efforts

2 Min Read
Computing

Xiaomi unveils self-developed Xuanjie O1 chip using 3nm process · TechNode

1 Min Read
//

World of Software is your one-stop website for the latest tech news and updates, follow us now to get the news that matters to you.

Quick Link

  • Privacy Policy
  • Terms of use
  • Advertise
  • Contact

Topics

  • Computing
  • Software
  • Press Release
  • Trending

Sign Up for Our Newsletter

Subscribe to our newsletter to get our newest articles instantly!

World of SoftwareWorld of Software
Follow US
Copyright © All Rights Reserved. World of Software.
Welcome Back!

Sign in to your account

Lost your password?