Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions pkg/api/osvdev.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package api

// OSV (Open Source Vulnerabilities) API/schema types.
//
// These mirror the osv.dev REST API (https://google.github.io/osv.dev/api/) and the
// OSV schema (https://ossf.github.io/osv-schema/) so that OSV tooling such as
// osv-scanner can consume our Lightwell advisories by pointing at /demo/osvdev
// instead of api.osv.dev. Field names use snake_case to match the real service.

// OsvPackage identifies an affected package.
type OsvPackage struct {
Ecosystem string `json:"ecosystem,omitempty"`
Name string `json:"name,omitempty"`
Purl string `json:"purl,omitempty"`
}

// OsvEvent is a single point in an affected range. Exactly one field is set.
type OsvEvent struct {
Introduced string `json:"introduced,omitempty"`
Fixed string `json:"fixed,omitempty"`
LastAffected string `json:"last_affected,omitempty"`
Limit string `json:"limit,omitempty"`
}

// OsvRange is an ordered set of version events for an ecosystem.
type OsvRange struct {
Type string `json:"type"`
Repo string `json:"repo,omitempty"`
Events []OsvEvent `json:"events"`
}

// OsvAffected describes a package and the versions it is affected in.
type OsvAffected struct {
Package OsvPackage `json:"package"`
Ranges []OsvRange `json:"ranges,omitempty"`
Versions []string `json:"versions,omitempty"`
}

// OsvReference is a URL reference for a vulnerability.
type OsvReference struct {
Type string `json:"type"`
URL string `json:"url"`
}

// OsvSeverity is a severity score for a vulnerability.
type OsvSeverity struct {
Type string `json:"type"`
Score string `json:"score"`
}

// OsvVulnerability is a full OSV record.
type OsvVulnerability struct {
SchemaVersion string `json:"schema_version,omitempty"`
ID string `json:"id"`
Modified string `json:"modified"`
Published string `json:"published,omitempty"`
Aliases []string `json:"aliases,omitempty"`
Summary string `json:"summary,omitempty"`
Details string `json:"details,omitempty"`
Severity []OsvSeverity `json:"severity,omitempty"`
Affected []OsvAffected `json:"affected,omitempty"`
References []OsvReference `json:"references,omitempty"`
}

// OsvQuery is the body of POST /v1/query and each entry of a batch query.
type OsvQuery struct {
Commit string `json:"commit,omitempty"`
Version string `json:"version,omitempty"`
Package OsvPackage `json:"package,omitempty"`
PageToken string `json:"page_token,omitempty"`
}

// OsvBatchQuery is the body of POST /v1/querybatch.
type OsvBatchQuery struct {
Queries []OsvQuery `json:"queries"`
}

// OsvVulnerabilityList is the response of POST /v1/query.
type OsvVulnerabilityList struct {
Vulns []OsvVulnerability `json:"vulns,omitempty"`
NextPageToken string `json:"next_page_token,omitempty"`
}

// OsvVulnStub is the lightweight vulnerability reference returned by querybatch.
type OsvVulnStub struct {
ID string `json:"id"`
Modified string `json:"modified"`
}

// OsvBatchResult is a single query's result within a batch response.
type OsvBatchResult struct {
Vulns []OsvVulnStub `json:"vulns,omitempty"`
NextPageToken string `json:"next_page_token,omitempty"`
}

// OsvBatchVulnerabilityList is the response of POST /v1/querybatch, index-aligned
// with the request queries.
type OsvBatchVulnerabilityList struct {
Results []OsvBatchResult `json:"results"`
}
1 change: 1 addition & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ type FeatureSet struct {
LightwellBeacon Feature `mapstructure:"lightwell_beacon"`
LightwellLens Feature `mapstructure:"lightwell_lens"`
LightwellStoreUploads Feature `mapstructure:"lightwell_store_uploads"`
LightwellOsvDemo Feature `mapstructure:"lightwell_osv_demo"`
AdminJfrogUpload Feature `mapstructure:"admin_jfrog_upload"`
}

Expand Down
62 changes: 62 additions & 0 deletions pkg/dao/dao_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pkg/dao/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ type LightwellAdvisoryDao interface {
SyncForRepository(ctx context.Context, repoConfigUUID string, repoName string, advisories []LightwellAdvisoryInput) error
ListByRepository(ctx context.Context, repoConfigUUID string) ([]LightwellAdvisoryInput, error)
List(ctx context.Context, offset int, limit int) ([]LightwellAdvisoryInput, int64, error)
ListForOsv(ctx context.Context) ([]models.LightwellAdvisory, error)
ListUnnotifiedAdvisories(ctx context.Context, repoConfigUUID string, orgID string) ([]LightwellNotificationData, error)
MarkAsNotified(ctx context.Context, repoConfigUUID string, orgID string, data []LightwellNotificationData) error
}
Expand Down
12 changes: 12 additions & 0 deletions pkg/dao/lightwell_advisory.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ func (d lightwellAdvisoryDaoImpl) List(ctx context.Context, offset int, limit in
return advisoryInputs(advisories), total, nil
}

// ListForOsv returns all advisories (with timestamps) for serving the osv.dev-compatible
// demo feed. Unlike List, it returns full models so callers can build OSV records that
// require modified/published timestamps.
func (d lightwellAdvisoryDaoImpl) ListForOsv(ctx context.Context) ([]models.LightwellAdvisory, error) {
var advisories []models.LightwellAdvisory
result := d.db.WithContext(ctx).Order("advisory_id ASC").Find(&advisories)
if result.Error != nil {
return nil, fmt.Errorf("failed to list advisories for osv feed: %w", result.Error)
}
return advisories, nil
}

func advisoryInputs(advisories []models.LightwellAdvisory) []LightwellAdvisoryInput {
inputs := make([]LightwellAdvisoryInput, len(advisories))
for i, a := range advisories {
Expand Down
4 changes: 4 additions & 0 deletions pkg/handler/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ func RegisterRoutes(ctx context.Context, engine *echo.Echo) {
}
}

// osv.dev-compatible demo feed is public (no identity/RBAC) and rooted at
// /demo/osvdev, so register it on the raw engine rather than the API group.
RegisterOsvDevRoutes(engine, dao.GetDaoRegistry(db.DB))

data, err := json.MarshalIndent(engine.Routes(), "", " ")
if err == nil {
log.Debug().Msg(string(data))
Expand Down
Loading