Skip to content

🔒 Security Audit

Perform security audits on project dependencies to detect known vulnerabilities (CVEs) and abandoned packages.

Composer Skills wraps composer audit and related capabilities into a set of methods covering the complete chain from "get raw output" to "structured parsing" to "filter high-risk vulnerabilities by severity". All structured methods are based on composer audit --format=json and automatically handle the non-zero exit code returned when vulnerabilities are found.

When to Use

  • 📦 Run an audit after composer install in CI/CD pipeline; block the build if vulnerabilities are found.
  • 🛠️ Operations patrol: periodically scan composer.lock to confirm no new CVEs have been disclosed for production dependencies.
  • 🧩 Dependency governance: find abandoned packages and plan replacements in advance.
  • ⚡ Incident response: quickly determine if the current project is affected when a CVE breaks out, including affected package versions and severity.

🔍 Audit Data Flow

Structured Return Types

AuditResult

Returned by AuditWithJSON, corresponds to the top-level structure of composer audit --format=json.

go
type AuditResult struct {
	Vulnerabilities []Vulnerability `json:"vulnerabilities"`
	Found           int             `json:"found"`
	Advisory        string          `json:"advisory,omitempty"`
	WithoutDev      bool            `json:"without-dev,omitempty"`
}
FieldTypeDescription
Vulnerabilities[]VulnerabilityList of vulnerabilities found
FoundintTotal number of vulnerabilities
AdvisorystringAdvisory identifier (may be empty)
WithoutDevboolWhether dev dependencies were excluded

Vulnerability

Individual vulnerability entry. When Abandoned=true, this entry represents an "abandoned package" rather than a traditional CVE.

go
type Vulnerability struct {
	Package     string   `json:"package"`
	Version     string   `json:"version"`
	Title       string   `json:"title"`
	Link        string   `json:"link"`
	CVE         []string `json:"cve,omitempty"`
	Advisory    string   `json:"advisory"`
	Abandoned   bool     `json:"abandoned,omitempty"`
	Severity    string   `json:"severity,omitempty"`
	Source      string   `json:"source,omitempty"`
	Affectedver string   `json:"affectedver,omitempty"`
}
FieldTypeDescription
PackagestringAffected package name
VersionstringCurrently installed version
TitlestringVulnerability title
LinkstringDetails link
CVE[]stringList of associated CVE identifiers
AdvisorystringAdvisory identifier
AbandonedboolWhether this is an abandoned package
SeveritystringSeverity: critical/high/medium/low
SourcestringData source
AffectedverstringAffected version range

AuditInfoResult

Returned by GetAuditInfo / GetAuditInfoWithOptions, provides more granular advisory information.

go
type AuditAdvisoryInfo struct {
	PackageName string `json:"package"`
	Version     string `json:"version"`
	Title       string `json:"title"`
	Severity    string `json:"severity"` // "critical", "high", "medium", "low"
	CVE         string `json:"cve,omitempty"`
	Link        string `json:"link,omitempty"`
	ReportedAt  string `json:"reportedAt,omitempty"`
}

type AuditInfoResult struct {
	Advisories []AuditAdvisoryInfo `json:"advisories"`
	Count      int                 `json:"count,omitempty"`
}

Difference Between Two Structured Results

AuditResult (from AuditWithJSON) directly maps Composer's raw JSON, including abandoned markers; AuditInfoResult (from GetAuditInfo) is an SDK-layer normalization with more consistent field names (e.g., PackageName), and automatically fills Count during parsing. Use the former when you need to determine "whether abandoned packages are included"; use the latter when you need to iterate through advisories for alert distribution.


Audit

🔒 Execute security audit, return raw text output.

Signature

go
func (c *Composer) Audit() (string, error)

Parameters

None.

Return Values

ValueTypeDescription
OutputstringStandard output from composer audit
ErrorerrorReturned on execution failure; also returned as error when Composer returns non-zero exit code due to found vulnerabilities

Example

go
package main

import (
	"fmt"
	"log"

	"github.com/scagogogo/composer-skills/pkg/composer"
)

func main() {
	comp, err := composer.New(composer.DefaultOptions())
	if err != nil {
		log.Fatalf("Initialization failed: %v", err)
	}

	output, err := comp.Audit()
	if err != nil {
		log.Fatalf("Security audit failed: %v", err)
	}
	fmt.Println("Security audit result:", output)
}

AuditWithJSON

🔒 Execute security audit and return structured JSON result.

Signature

go
func (c *Composer) AuditWithJSON() (*AuditResult, error)

Parameters

None.

Return Values

ValueTypeDescription
Result*AuditResultParsed audit result, including vulnerability list and count
ErrorerrorReturned on execution or JSON parsing failure

Example

go
result, err := comp.AuditWithJSON()
if err != nil {
	log.Fatalf("Security audit failed: %v", err)
}
fmt.Printf("Found %d vulnerabilities\n", result.Found)
for _, vuln := range result.Vulnerabilities {
	fmt.Printf("Vulnerability: %s %s\n", vuln.Package, vuln.Title)
	fmt.Printf("Severity: %s\n", vuln.Severity)
	fmt.Printf("Details: %s\n\n", vuln.Link)
}

AuditWithoutDev

🔒 Audit only production dependencies, excluding dev dependencies (require-dev).

Signature

go
func (c *Composer) AuditWithoutDev() (string, error)

Parameters

None.

Return Values

ValueTypeDescription
OutputstringOutput from composer audit --no-dev
ErrorerrorReturned on execution failure

Example

go
output, err := comp.AuditWithoutDev()
if err != nil {
	log.Fatalf("Security audit failed: %v", err)
}
fmt.Println("Production dependency security audit result:", output)

Equivalent Command

composer audit --no-dev. Production environments typically don't deploy dev dependencies, so auditing production dependencies reflects the actual risk surface.


AuditWithFormat

🔒 Output audit result in specified format.

Signature

go
func (c *Composer) AuditWithFormat(format string) (string, error)

Parameters

ParameterTypeDescription
formatstringOutput format, e.g., json, table, plain

Return Values

ValueTypeDescription
OutputstringOutput from composer audit --format=FORMAT
ErrorerrorReturned on execution failure

Example

go
// Output in table format
output, err := comp.AuditWithFormat("table")
if err != nil {
	log.Fatalf("Security audit failed: %v", err)
}
fmt.Println(output)

// Output in plain text format
output, err = comp.AuditWithFormat("plain")
if err != nil {
	log.Fatalf("Security audit failed: %v", err)
}
fmt.Println(output)

HasVulnerabilities

🔒 Quickly determine if the project has any security vulnerabilities.

Signature

go
func (c *Composer) HasVulnerabilities() (bool, error)

Parameters

None.

Return Values

ValueTypeDescription
Has vulnerabilitiesbooltrue means vulnerabilities exist
ErrorerrorReturned when unexpected error occurs during check

Implementation Note

Internally calls AuditWithJSON and determines based on result.Found > 0. When Composer returns non-zero exit code due to found vulnerabilities, it checks if the error message contains Found and vulnerabilities keywords; if so, it's also considered "has vulnerabilities" without propagating the error.

Example

go
hasVulns, err := comp.HasVulnerabilities()
if err != nil {
	log.Fatalf("Failed to check vulnerabilities: %v", err)
}
if hasVulns {
	fmt.Println("Warning: Security vulnerabilities exist in the project!")
} else {
	fmt.Println("No security vulnerabilities found in the project.")
}

GetHighSeverityVulnerabilities

🔒 Filter out high-severity vulnerabilities (high or critical) for prioritized fixing.

Signature

go
func (c *Composer) GetHighSeverityVulnerabilities() ([]Vulnerability, error)

Parameters

None.

Return Values

ValueTypeDescription
High-severity vulnerabilities[]VulnerabilityList of vulnerabilities with Severity as high or critical; empty slice if none
ErrorerrorReturned on audit or parsing failure

Example

go
highVulns, err := comp.GetHighSeverityVulnerabilities()
if err != nil {
	log.Fatalf("Failed to get high-severity vulnerabilities: %v", err)
}
if len(highVulns) > 0 {
	fmt.Printf("Found %d high-severity vulnerabilities:\n", len(highVulns))
	for _, vuln := range highVulns {
		fmt.Printf("Package: %s Version: %s\n", vuln.Package, vuln.Version)
		fmt.Printf("Vulnerability: %s\n", vuln.Title)
		fmt.Printf("Details: %s\n\n", vuln.Link)
	}
} else {
	fmt.Println("No high-severity vulnerabilities found.")
}

AuditLock

🔒 Audit a specified composer.lock file, usable for projects that haven't installed dependencies yet.

Signature

go
func (c *Composer) AuditLock(lockFilePath string) (string, error)

Parameters

ParameterTypeDescription
lockFilePathstringPath to composer.lock file; pass empty string to audit the lock file in current directory

Return Values

ValueTypeDescription
OutputstringAudit output
ErrorerrorReturned on execution failure

Example

go
// Audit the lock file in current project
output, err := comp.AuditLock("")
if err != nil {
	log.Fatalf("Audit lock file failed: %v", err)
}
fmt.Println(output)

// Audit another project's lock file
output, err = comp.AuditLock("/path/to/other/project/composer.lock")
if err != nil {
	log.Fatalf("Audit lock file failed: %v", err)
}
fmt.Println(output)

Path Parameter

When lockFilePath is non-empty, the path is passed directly to composer audit <path> as a positional argument. Ensure the path is correct and the file exists.


GetAbandonedPackages

🔒 Get list of packages marked as "abandoned".

Signature

go
func (c *Composer) GetAbandonedPackages() ([]Vulnerability, error)

Parameters

None.

Return Values

ValueTypeDescription
Abandoned packages[]VulnerabilityList of entries with Abandoned=true; empty slice if none
ErrorerrorReturned on audit or parsing failure

Example

go
abandoned, err := comp.GetAbandonedPackages()
if err != nil {
	log.Fatalf("Failed to get abandoned packages: %v", err)
}
if len(abandoned) > 0 {
	fmt.Printf("Found %d abandoned packages:\n", len(abandoned))
	for _, pkg := range abandoned {
		fmt.Printf("Package: %s Version: %s\n", pkg.Package, pkg.Version)
		fmt.Printf("Details: %s\n\n", pkg.Link)
	}
	fmt.Println("Consider replacing these packages to avoid potential security risks.")
} else {
	fmt.Println("No abandoned packages found.")
}

Reusing Vulnerability Type

Abandoned packages are not traditional vulnerabilities, but the SDK reuses the Vulnerability struct, distinguishing them via the Abandoned field. Check this field first when iterating if you need to handle them separately.


Advanced

AuditWithOptions

Use when you need to combine multiple audit flags for maximum flexibility.

go
func (c *Composer) AuditWithOptions(options map[string]string) (string, error)
ParameterTypeDescription
optionsmap[string]stringOptions map; keys are option names, values are option values (pass empty string for flag options without values)
go
options := map[string]string{
	"no-dev":  "",
	"format":  "json",
	"locked":  "",
}
output, err := comp.AuditWithOptions(options)
if err != nil {
	log.Fatalf("Security audit failed: %v", err)
}
fmt.Println(output)

GetAuditInfo / GetAuditInfoWithOptions

Returns normalized AuditInfoResult, suitable for scenarios where you need to distribute alerts per advisory (e.g., pushing to monitoring systems).

go
func (c *Composer) GetAuditInfo() (*AuditInfoResult, error)
func (c *Composer) GetAuditInfoWithOptions(options map[string]string) (*AuditInfoResult, error)
go
result, err := comp.GetAuditInfo()
if err != nil {
	log.Fatalf("Security audit failed: %v", err)
}
for _, adv := range result.Advisories {
	fmt.Printf("Vulnerability: %s (%s) - %s\n", adv.PackageName, adv.Severity, adv.Title)
}

RunAudit

dependencies.go also provides RunAudit(), equivalent to Audit(), which can be called in pairs in dependency management workflows.

Released under the MIT License