🔒 Security Advisories
Query disclosed security vulnerabilities (CVE / GHSA) on Packagist. Three methods support "full fetch", "by package", and "incremental by time", returning a unified AdvisoriesResponse structure. This is a core capability for dependency security monitoring, SCA tool integration, and CI vulnerability gating.
Endpoint Change
The old endpoint https://packagist.org/advisories.json has been deprecated by Packagist (returns 404). The SDK now uses the new endpoint https://packagist.org/api/security-advisories/, with query parameters updatedSince and packages[].
When to Use
- 🔒 CI gating: Query project dependencies for new advisories on each build, block if found.
- 📡 Security monitoring: Daily incremental fetch with
GetSecurityAdvisoriesSince(last time), alert when new advisories appear. - 🧩 SCA scanning: Call
GetSecurityAdvisoriesForPackagesfor a set of packages, determine if current versions fall within affected ranges. - 🛠️ Vulnerability database sync: Full fetch then store, periodically update incrementally.
Data Models
AdvisoriesResponse
pkg/domain/advisory.go, corresponds to /api/security-advisories/ response.
type AdvisoriesResponse struct {
Advisories map[string][]*Advisory `json:"advisories"`
}| Field | Type | Description |
|---|---|---|
Advisories | map[string][]*Advisory | Key is package name (e.g., symfony/http-foundation), value is the list of advisories for that package |
Advisory
Single security advisory.
type Advisory struct {
AdvisoryID string `json:"advisoryId"`
PackageName string `json:"packageName"`
RemoteID string `json:"remoteId"`
Title string `json:"title"`
Link string `json:"link"`
Cve string `json:"cve"`
AffectedVersions string `json:"affectedVersions"`
Source string `json:"source"`
ReportedAt string `json:"reportedAt"`
ComposerRepository string `json:"composerRepository"`
Sources []*Source `json:"sources"`
}| Field | Type | Description |
|---|---|---|
AdvisoryID | string | Unique advisory identifier, e.g., PKSA-38s9-s9dj |
PackageName | string | Affected package name |
RemoteID | string | Remote system ID, e.g., CVE-2022-24894 |
Title | string | Advisory title |
Link | string | Detail link (often points to GitHub Advisory) |
Cve | string | CVE number |
AffectedVersions | string | Affected version range (Composer constraint syntax, e.g., >=5.4.0,<5.4.19|>=6.0.0,<6.0.4) |
Source | string | Source platform, e.g., GitHub |
ReportedAt | string | Report time, ISO 8601 |
ComposerRepository | string | Related repository, e.g., packagist |
Sources | []*Source | Multi-source reference list |
Source
type Source struct {
Name string `json:"name"`
RemoteID string `json:"remoteId"`
}| Field | Type | Description |
|---|---|---|
Name | string | Source name, e.g., GitHub, NVD |
RemoteID | string | Remote ID from the source platform, e.g., GHSA-rc93-5vf2-xh7q |
GetSecurityAdvisories
🔒 Get all known security advisories. Corresponds to GET https://packagist.org/api/security-advisories/?updatedSince=0.
Signature
func (c *ComposerClient) GetSecurityAdvisories() (*domain.AdvisoriesResponse, error)Parameters
None.
Return Values
| Value | Type | Description |
|---|---|---|
| Result | *domain.AdvisoriesResponse | Security advisory mapping grouped by package name |
| Error | error | Returned on HTTP failure, non-200, or JSON parsing failure |
Implementation Notes
The new endpoint requires at least one query parameter, otherwise returns 400. The SDK internally uses updatedSince=0, indicating fetch all known advisories.
Example
package main
import (
"fmt"
"log"
"time"
"github.com/scagogogo/composer-skills/pkg/client"
)
func main() {
c := client.NewComposerClient(60 * time.Second) // Full data is large, give sufficient timeout
adv, err := c.GetSecurityAdvisories()
if err != nil {
log.Fatalf("Failed to get security advisories: %v", err)
}
fmt.Printf("Involving %d packages\n", len(adv.Advisories))
total := 0
for pkg, list := range adv.Advisories {
total += len(list)
fmt.Printf("- %s: %d advisories\n", pkg, len(list))
}
fmt.Printf("Total advisories: %d\n", total)
}Data Volume
Full advisory response can be large (tens of MB). Suggestions: ① Set timeout to 60s or more; ② After initial full fetch, use GetSecurityAdvisoriesSince for incremental updates; ③ For large-scale processing, consider using the lower-level Repository.ListSecurityAdvisories with a proxy.
GetSecurityAdvisoriesForPackages
🔒 Get security advisories for a specified set of packages. Corresponds to GET https://packagist.org/api/security-advisories/?packages[]={name}.
Signature
func (c *ComposerClient) GetSecurityAdvisoriesForPackages(packageNames []string) (*domain.AdvisoriesResponse, error)Parameters
| Parameter | Type | Description |
|---|---|---|
packageNames | []string | Package name list, each sent as a packages[] query parameter |
Return Values
| Value | Type | Description |
|---|---|---|
| Result | *domain.AdvisoriesResponse | Advisories only for requested packages (key is the requested package name) |
| Error | error | Returned on HTTP failure, non-200, or JSON parsing failure |
Example
pkgs := []string{
"symfony/http-foundation",
"symfony/console",
"monolog/monolog",
}
adv, err := c.GetSecurityAdvisoriesForPackages(pkgs)
if err != nil {
log.Fatal(err)
}
for pkg, list := range adv.Advisories {
fmt.Printf("=== %s ===\n", pkg)
for _, a := range list {
fmt.Printf(" [%s] %s\n", a.Cve, a.Title)
fmt.Printf(" Affected versions: %s\n", a.AffectedVersions)
fmt.Printf(" Details: %s\n\n", a.Link)
}
}Vulnerability Determination Logic
After obtaining AffectedVersions (e.g., >=5.4.0,<5.4.19|>=6.0.0,<6.0.4), you need to combine it with the project's actual installed version to determine if affected. The SDK does not perform version constraint parsing. We recommend using Composer CLI version constraint parsing or a third-party constraint library.
GetSecurityAdvisoriesSince
🔒 Get security advisories updated since a specified time (incremental query). Corresponds to GET https://packagist.org/api/security-advisories/?updatedSince={timestamp}.
Signature
func (c *ComposerClient) GetSecurityAdvisoriesSince(updatedSince time.Time) (*domain.AdvisoriesResponse, error)Parameters
| Parameter | Type | Description |
|---|---|---|
updatedSince | time.Time | Start time; SDK takes its Unix() second-level timestamp as the updatedSince query parameter |
Return Values
| Value | Type | Description |
|---|---|---|
| Result | *domain.AdvisoriesResponse | Advisories updated after that time point |
| Error | error | Returned on HTTP failure, non-200, or JSON parsing failure |
Example
// Fetch advisories updated in the last 24 hours
since := time.Now().Add(-24 * time.Hour)
adv, err := c.GetSecurityAdvisoriesSince(since)
if err != nil {
log.Fatal(err)
}
for pkg, list := range adv.Advisories {
for _, a := range list {
fmt.Printf("[New/Updated] %s: %s (%s)\n", pkg, a.Title, a.ReportedAt)
}
}Incremental Sync
The typical approach is to persist "last query time", then use it as updatedSince each time:
last := loadLastSyncTime() // Read from database/file
adv, err := c.GetSecurityAdvisoriesSince(last)
// Process adv.Advisories ...
saveLastSyncTime(time.Now()) // Record new checkpointTimestamp Precision
ComposerClient.GetSecurityAdvisoriesSince uses second-level Unix() timestamp; while the lower-level Repository.ListSecurityAdvisories uses millisecond-level UnixMilli(). Both hit the same endpoint, only differing in timestamp precision. Choose based on your semantic needs.
Advanced Topics
Comparison of Three Methods
| Method | Query Parameter | Use Case |
|---|---|---|
GetSecurityAdvisories | updatedSince=0 | Initial full fetch to database |
GetSecurityAdvisoriesForPackages | packages[] | Query specific packages on demand (common for CI gating) |
GetSecurityAdvisoriesSince | updatedSince={ts} | Long-term incremental sync |
Difference from Composer CLI Audit
- Composer CLI
Auditaudits localcomposer.lock, targeting actual locked versions for the current project; requires PHP / composer binary. - Methods in this section query Packagist full repository advisories, targeting package names rather than your installed versions; pure HTTP, no PHP needed.
Both are complementary: CLI audit precisely determines "whether current version is affected", SDK methods provide a panoramic view of "which advisories exist, which packages are affected".
🔗 Related
- 🏗️ Lower-level implementation see Repository.ListSecurityAdvisories / ListAdvisories.
- 📦 Package names in advisories can be further queried with GetPackage for details.