|
1 | 1 | package main
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "context" |
4 | 5 | "errors"
|
5 | 6 | "fmt"
|
6 | 7 | "os"
|
7 | 8 | "regexp"
|
8 | 9 |
|
| 10 | + "github.com/Masterminds/semver" |
9 | 11 | "github.com/jackc/pgx/v4"
|
10 | 12 | "github.com/jackc/pgx/v4/pgxpool"
|
| 13 | + "github.com/quay/claircore" |
11 | 14 | "github.com/quay/zlog"
|
12 | 15 | "github.com/urfave/cli/v2"
|
13 | 16 | )
|
@@ -69,6 +72,15 @@ var AdminCmd = &cli.Command{
|
69 | 72 | Description: "Tasks that may be useful on occasion",
|
70 | 73 | Usage: "run one-off task",
|
71 | 74 | ArgsUsage: "\b",
|
| 75 | + Subcommands: []*cli.Command{ |
| 76 | + { |
| 77 | + Name: "update-golang-packages", |
| 78 | + Description: "This task will update the golang packages in the `package` table with the `norm_versions` and `norm_kind`.\n" + |
| 79 | + "Relevant package names are gleaned from the vulnerabilities in the matchers `vuln` table.\n\n", |
| 80 | + Usage: "update golang packages in the indexer DB", |
| 81 | + Action: updateGoPackages, |
| 82 | + }, |
| 83 | + }, |
72 | 84 | },
|
73 | 85 | },
|
74 | 86 | }
|
@@ -282,3 +294,148 @@ func adminPre473(c *cli.Context) error {
|
282 | 294 | return nil
|
283 | 295 | })
|
284 | 296 | }
|
| 297 | + |
| 298 | +func updateGoPackages(c *cli.Context) error { |
| 299 | + const ( |
| 300 | + getPackageNames = "SELECT DISTINCT package_name FROM vuln WHERE updater = 'osv/go'" |
| 301 | + getPackages = "SELECT id, version FROM package WHERE name = $1 and norm_version IS NULL" |
| 302 | + updatePackages = "UPDATE package SET norm_version=$1::int[], norm_kind=$2 WHERE id = $3 and norm_version IS NULL" |
| 303 | + ) |
| 304 | + |
| 305 | + ctx := c.Context |
| 306 | + fi, err := os.Stat(c.Path("config")) |
| 307 | + switch { |
| 308 | + case !errors.Is(err, nil): |
| 309 | + return fmt.Errorf("bad config: %w", err) |
| 310 | + case fi.IsDir(): |
| 311 | + return fmt.Errorf("bad config: is a directory") |
| 312 | + } |
| 313 | + cfg, err := loadConfig(c.Path("config")) |
| 314 | + if err != nil { |
| 315 | + return fmt.Errorf("error loading config: %w", err) |
| 316 | + } |
| 317 | + matcherPool, err := createConnPool(ctx, cfg.Matcher.ConnString, 2) |
| 318 | + if err != nil { |
| 319 | + return fmt.Errorf("error creating indexer pool: %w", err) |
| 320 | + } |
| 321 | + defer matcherPool.Close() |
| 322 | + packageNames := []string{} |
| 323 | + err = matcherPool.AcquireFunc(ctx, func(conn *pgxpool.Conn) error { |
| 324 | + rows, err := conn.Query(ctx, getPackageNames) |
| 325 | + if err != nil { |
| 326 | + return fmt.Errorf("error getting package_name list: %w", err) |
| 327 | + } |
| 328 | + defer rows.Close() |
| 329 | + for rows.Next() { |
| 330 | + var p string |
| 331 | + err := rows.Scan(&p) |
| 332 | + if err != nil { |
| 333 | + return err |
| 334 | + } |
| 335 | + packageNames = append(packageNames, p) |
| 336 | + } |
| 337 | + return nil |
| 338 | + }) |
| 339 | + if err != nil { |
| 340 | + return fmt.Errorf("could not get package names from matcher DB %w", err) |
| 341 | + } |
| 342 | + indexerPool, err := createConnPool(ctx, cfg.Indexer.ConnString, 2) |
| 343 | + if err != nil { |
| 344 | + return fmt.Errorf("error creating indexer pool: %w", err) |
| 345 | + } |
| 346 | + defer indexerPool.Close() |
| 347 | + |
| 348 | + for _, p := range packageNames { |
| 349 | + err := indexerPool.AcquireFunc(ctx, func(conn *pgxpool.Conn) error { |
| 350 | + rows, err := conn.Query(ctx, getPackages, p) |
| 351 | + if err != nil { |
| 352 | + return fmt.Errorf("could not get packages for %s: %w", p, err) |
| 353 | + } |
| 354 | + defer rows.Close() |
| 355 | + for rows.Next() { |
| 356 | + var ( |
| 357 | + id int64 |
| 358 | + version string |
| 359 | + ) |
| 360 | + err := rows.Scan(&id, &version) |
| 361 | + if err != nil { |
| 362 | + return err |
| 363 | + } |
| 364 | + ctx = zlog.ContextWithValues(ctx, "package_name", p, "version", version) |
| 365 | + zlog.Debug(ctx). |
| 366 | + Msg("working on version") |
| 367 | + |
| 368 | + var nv claircore.Version |
| 369 | + ver, err := semver.NewVersion(version) |
| 370 | + switch { |
| 371 | + case errors.Is(err, nil): |
| 372 | + nv = fromSemver(ver) |
| 373 | + default: |
| 374 | + zlog.Warn(ctx). |
| 375 | + Err(err). |
| 376 | + Msg("error parsing semver") |
| 377 | + continue |
| 378 | + } |
| 379 | + var ( |
| 380 | + vKind *string |
| 381 | + vNorm []int32 |
| 382 | + ) |
| 383 | + if nv.Kind != "" { |
| 384 | + vKind = &nv.Kind |
| 385 | + vNorm = nv.V[:] |
| 386 | + } |
| 387 | + |
| 388 | + tag, err := indexerPool.Exec(ctx, updatePackages, vNorm, vKind, id) |
| 389 | + if err != nil { |
| 390 | + return fmt.Errorf("error updating packages: %w", err) |
| 391 | + } |
| 392 | + zlog.Info(ctx). |
| 393 | + Int64("package_id", id). |
| 394 | + Int64("rows affected", tag.RowsAffected()). |
| 395 | + Msg("successfully updated package row") |
| 396 | + } |
| 397 | + return nil |
| 398 | + }) |
| 399 | + if err != nil { |
| 400 | + return fmt.Errorf("error acquiring pool conn: %w", err) |
| 401 | + } |
| 402 | + } |
| 403 | + return nil |
| 404 | +} |
| 405 | + |
| 406 | +func createConnPool(ctx context.Context, dsn string, maxConns int32) (*pgxpool.Pool, error) { |
| 407 | + pgcfg, err := pgxpool.ParseConfig(dsn) |
| 408 | + if err != nil { |
| 409 | + return nil, fmt.Errorf("error parsing dsn: %w", err) |
| 410 | + } |
| 411 | + zlog.Info(ctx). |
| 412 | + Str("host", pgcfg.ConnConfig.Host). |
| 413 | + Str("database", pgcfg.ConnConfig.Database). |
| 414 | + Str("user", pgcfg.ConnConfig.User). |
| 415 | + Uint16("port", pgcfg.ConnConfig.Port). |
| 416 | + Msg("using discovered connection params") |
| 417 | + |
| 418 | + zlog.Debug(ctx). |
| 419 | + Int32("pool size", maxConns). |
| 420 | + Msg("resizing pool") |
| 421 | + pgcfg.MaxConns = int32(maxConns) |
| 422 | + pool, err := pgxpool.ConnectConfig(ctx, pgcfg) |
| 423 | + if err != nil { |
| 424 | + return nil, fmt.Errorf("error creating pool: %w", err) |
| 425 | + } |
| 426 | + if err := pool.Ping(ctx); err != nil { |
| 427 | + return nil, fmt.Errorf("error connecting to database: %w", err) |
| 428 | + } |
| 429 | + return pool, nil |
| 430 | +} |
| 431 | + |
| 432 | +// FromSemVer is copied from the gobin package. It converts |
| 433 | +// a semver.Version to a claircore.Version. |
| 434 | +func fromSemver(v *semver.Version) (out claircore.Version) { |
| 435 | + out.Kind = `semver` |
| 436 | + // Leave a leading epoch, for good measure. |
| 437 | + out.V[1] = int32(v.Major()) |
| 438 | + out.V[2] = int32(v.Minor()) |
| 439 | + out.V[3] = int32(v.Patch()) |
| 440 | + return out |
| 441 | +} |
0 commit comments