Skip to content

🔒 安全公告

查询 Packagist 上已披露的安全漏洞(CVE / GHSA)。三个方法分别支持「全量拉取」「按包查」「按时间增量查」,返回结构统一为 AdvisoriesResponse,是依赖安全监控、SCA 工具集成、CI 漏洞门禁的核心能力。

端点变更

旧端点 https://packagist.org/advisories.json 已被 Packagist 废弃(返回 404)。SDK 改用新端点 https://packagist.org/api/security-advisories/,查询参数为 updatedSincepackages[]

何时使用

  • 🔒 CI 门禁:每次构建时查项目依赖是否有新公告,有则阻断。
  • 📡 安全监控:每日增量拉取 GetSecurityAdvisoriesSince(上次时间),发现新公告即告警。
  • 🧩 SCA 扫描:对一组包调 GetSecurityAdvisoriesForPackages,判断当前版本是否在受影响范围内。
  • 🛠️ 漏洞库同步:全量拉取后入库,定期增量更新。

数据模型

AdvisoriesResponse

pkg/domain/advisory.go,对应 /api/security-advisories/ 响应。

go
type AdvisoriesResponse struct {
    Advisories map[string][]*Advisory `json:"advisories"`
}
字段类型说明
Advisoriesmap[string][]*Advisory键为包名(如 symfony/http-foundation),值为该包的公告列表

Advisory

单个安全公告。

go
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"`
}
字段类型说明
AdvisoryIDstring公告唯一标识,如 PKSA-38s9-s9dj
PackageNamestring受影响的包名
RemoteIDstring远程系统 ID,如 CVE-2022-24894
Titlestring公告标题
Linkstring详情链接(常指向 GitHub Advisory)
CvestringCVE 编号
AffectedVersionsstring受影响版本范围(Composer 约束语法,如 >=5.4.0,<5.4.19|>=6.0.0,<6.0.4
Sourcestring来源平台,如 GitHub
ReportedAtstring报告时间,ISO 8601
ComposerRepositorystring相关仓库,如 packagist
Sources[]*Source多来源引用列表

Source

go
type Source struct {
    Name     string `json:"name"`
    RemoteID string `json:"remoteId"`
}
字段类型说明
Namestring来源名称,如 GitHubNVD
RemoteIDstring来源平台的远程 ID,如 GHSA-rc93-5vf2-xh7q

GetSecurityAdvisories

🔒 获取全部已知安全公告。对应 GET https://packagist.org/api/security-advisories/?updatedSince=0

签名

go
func (c *ComposerClient) GetSecurityAdvisories() (*domain.AdvisoriesResponse, error)

参数

无。

返回值

类型说明
结果*domain.AdvisoriesResponse按包名分组的安全公告映射
错误errorHTTP 失败、非 200、JSON 解析失败时返回

实现说明

新端点要求至少带一个查询参数,否则返回 400。SDK 内部固定使用 updatedSince=0,表示获取所有已知公告。

示例

go
package main

import (
    "fmt"
    "log"
    "time"

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

func main() {
    c := client.NewComposerClient(60 * time.Second) // 全量数据较大,超时给足

    adv, err := c.GetSecurityAdvisories()
    if err != nil {
        log.Fatalf("获取安全公告失败: %v", err)
    }
    fmt.Printf("涉及 %d 个包\n", len(adv.Advisories))
    total := 0
    for pkg, list := range adv.Advisories {
        total += len(list)
        fmt.Printf("- %s: %d 条公告\n", pkg, len(list))
    }
    fmt.Printf("公告总数: %d\n", total)
}

数据量

全量公告响应可能较大(数十 MB),建议:① 超时设到 60s 以上;② 首次全量后改用 GetSecurityAdvisoriesSince 做增量;③ 大规模处理时考虑用底层 Repository.ListSecurityAdvisories 配合代理。


GetSecurityAdvisoriesForPackages

🔒 获取指定一组包的安全公告。对应 GET https://packagist.org/api/security-advisories/?packages[]={name}

签名

go
func (c *ComposerClient) GetSecurityAdvisoriesForPackages(packageNames []string) (*domain.AdvisoriesResponse, error)

参数

参数类型说明
packageNames[]string包名列表,每个作为 packages[] 查询参数发出

返回值

类型说明
结果*domain.AdvisoriesResponse仅包含请求包的公告(键为请求的包名)
错误errorHTTP 失败、非 200、JSON 解析失败时返回

示例

go
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("  受影响版本: %s\n", a.AffectedVersions)
        fmt.Printf("  详情: %s\n\n", a.Link)
    }
}

漏洞判断逻辑

拿到 AffectedVersions(如 >=5.4.0,<5.4.19|>=6.0.0,<6.0.4)后,需要结合项目实际安装的版本判断是否受影响。SDK 不做版本约束解析,建议配合 Composer CLI 的版本约束解析 或第三方约束库使用。


GetSecurityAdvisoriesSince

🔒 获取自指定时间以来更新的安全公告(增量查询)。对应 GET https://packagist.org/api/security-advisories/?updatedSince={timestamp}

签名

go
func (c *ComposerClient) GetSecurityAdvisoriesSince(updatedSince time.Time) (*domain.AdvisoriesResponse, error)

参数

参数类型说明
updatedSincetime.Time起始时间,SDK 取其 Unix() 秒级时间戳作为 updatedSince 查询参数

返回值

类型说明
结果*domain.AdvisoriesResponse该时间点之后有过更新的公告
错误errorHTTP 失败、非 200、JSON 解析失败时返回

示例

go
// 拉取最近 24 小时内更新的公告
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("[新/更新] %s: %s (%s)\n", pkg, a.Title, a.ReportedAt)
    }
}

增量同步

典型做法是持久化「上次查询时间」,每次以它为 updatedSince

go
last := loadLastSyncTime() // 从数据库/文件读取
adv, err := c.GetSecurityAdvisoriesSince(last)
// 处理 adv.Advisories ...
saveLastSyncTime(time.Now()) // 记录新的检查点

时间戳精度

ComposerClient.GetSecurityAdvisoriesSince 使用秒级 Unix() 时间戳;而底层 Repository.ListSecurityAdvisories 使用毫秒级 UnixMilli()。两者命中同一端点,仅时间戳精度不同,按你的语义需求选择。

进阶

三个方法的对比

方法查询参数适用
GetSecurityAdvisoriesupdatedSince=0首次全量入库
GetSecurityAdvisoriesForPackagespackages[]按需查指定包(CI 门禁常用)
GetSecurityAdvisoriesSinceupdatedSince={ts}长期增量同步

与 Composer CLI audit 的区别

  • Composer CLI 的 Audit 审计的是本地 composer.lock,针对当前项目实际锁定的版本;需要 PHP / composer 二进制。
  • 本节方法查的是 Packagist 全库公告,针对包名而非你安装的版本;纯 HTTP,无需 PHP。

两者互补:CLI audit 精确判断「当前版本是否受影响」,SDK 方法提供「有哪些公告、影响哪些包」的全景视图。

🔗 相关

基于 MIT 许可证发布