pgsql: Add detector to database schema
'detector' table is added to store the metadata of detectors. 'layer_feature', 'layer_namespace', and 'ancestry_feature' tables are modified to store the detection relationship between the feature/namespace with the detector.
This commit is contained in:
parent
db2db8bbe8
commit
dca2d4e597
@ -14,12 +14,11 @@
|
|||||||
|
|
||||||
package migrations
|
package migrations
|
||||||
|
|
||||||
import "github.com/remind101/migrate"
|
var (
|
||||||
|
// entities are the basic building blocks to relate the vulnerabilities with
|
||||||
func init() {
|
// the ancestry.
|
||||||
RegisterMigration(migrate.Migration{
|
entities = MigrationQuery{
|
||||||
ID: 1,
|
Up: []string{
|
||||||
Up: migrate.Queries([]string{
|
|
||||||
// namespaces
|
// namespaces
|
||||||
`CREATE TABLE IF NOT EXISTS namespace (
|
`CREATE TABLE IF NOT EXISTS namespace (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
@ -42,40 +41,72 @@ func init() {
|
|||||||
namespace_id INT REFERENCES namespace,
|
namespace_id INT REFERENCES namespace,
|
||||||
feature_id INT REFERENCES feature,
|
feature_id INT REFERENCES feature,
|
||||||
UNIQUE (namespace_id, feature_id));`,
|
UNIQUE (namespace_id, feature_id));`,
|
||||||
|
},
|
||||||
|
Down: []string{
|
||||||
|
`DROP TABLE IF EXISTS namespace, feature, namespaced_feature CASCADE;`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// detector is analysis extensions used by the worker.
|
||||||
|
detector = MigrationQuery{
|
||||||
|
Up: []string{
|
||||||
|
// Detector Type
|
||||||
|
`CREATE TYPE detector_type AS ENUM ('namespace', 'feature');`,
|
||||||
|
|
||||||
|
// Detector
|
||||||
|
`CREATE TABLE IF NOT EXISTS detector (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
version TEXT NOT NULL,
|
||||||
|
dtype detector_type NOT NULL,
|
||||||
|
UNIQUE (name, version, dtype));`,
|
||||||
|
},
|
||||||
|
Down: []string{
|
||||||
|
`DROP TABLE IF EXISTS detector CASCADE;`,
|
||||||
|
`DROP TYPE IF EXISTS detector_type;`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// layer contains all metadata and scanned features and namespaces.
|
||||||
|
layer = MigrationQuery{
|
||||||
|
Up: []string{
|
||||||
// layers
|
// layers
|
||||||
`CREATE TABLE IF NOT EXISTS layer(
|
`CREATE TABLE IF NOT EXISTS layer(
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
hash TEXT NOT NULL UNIQUE);`,
|
hash TEXT NOT NULL UNIQUE);`,
|
||||||
|
|
||||||
|
`CREATE TABLE IF NOT EXISTS layer_detector(
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
layer_id INT REFERENCES layer ON DELETE CASCADE,
|
||||||
|
detector_id INT REFERENCES detector ON DELETE CASCADE,
|
||||||
|
UNIQUE(layer_id, detector_id));`,
|
||||||
|
`CREATE INDEX ON layer_detector(layer_id);`,
|
||||||
|
|
||||||
`CREATE TABLE IF NOT EXISTS layer_feature (
|
`CREATE TABLE IF NOT EXISTS layer_feature (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
layer_id INT REFERENCES layer ON DELETE CASCADE,
|
layer_id INT REFERENCES layer ON DELETE CASCADE,
|
||||||
feature_id INT REFERENCES feature ON DELETE CASCADE,
|
feature_id INT REFERENCES feature ON DELETE CASCADE,
|
||||||
|
detector_id INT REFERENCES detector ON DELETE CASCADE,
|
||||||
UNIQUE (layer_id, feature_id));`,
|
UNIQUE (layer_id, feature_id));`,
|
||||||
`CREATE INDEX ON layer_feature(layer_id);`,
|
`CREATE INDEX ON layer_feature(layer_id);`,
|
||||||
|
|
||||||
`CREATE TABLE IF NOT EXISTS layer_lister (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
layer_id INT REFERENCES layer ON DELETE CASCADE,
|
|
||||||
lister TEXT NOT NULL,
|
|
||||||
UNIQUE (layer_id, lister));`,
|
|
||||||
`CREATE INDEX ON layer_lister(layer_id);`,
|
|
||||||
|
|
||||||
`CREATE TABLE IF NOT EXISTS layer_detector (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
layer_id INT REFERENCES layer ON DELETE CASCADE,
|
|
||||||
detector TEXT,
|
|
||||||
UNIQUE (layer_id, detector));`,
|
|
||||||
`CREATE INDEX ON layer_detector(layer_id);`,
|
|
||||||
|
|
||||||
`CREATE TABLE IF NOT EXISTS layer_namespace (
|
`CREATE TABLE IF NOT EXISTS layer_namespace (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
layer_id INT REFERENCES layer ON DELETE CASCADE,
|
layer_id INT REFERENCES layer ON DELETE CASCADE,
|
||||||
namespace_id INT REFERENCES namespace ON DELETE CASCADE,
|
namespace_id INT REFERENCES namespace ON DELETE CASCADE,
|
||||||
|
detector_id INT REFERENCES detector ON DELETE CASCADE,
|
||||||
UNIQUE (layer_id, namespace_id));`,
|
UNIQUE (layer_id, namespace_id));`,
|
||||||
`CREATE INDEX ON layer_namespace(layer_id);`,
|
`CREATE INDEX ON layer_namespace(layer_id);`,
|
||||||
|
},
|
||||||
|
Down: []string{
|
||||||
|
`DROP TABLE IF EXISTS layer, layer_detector, layer_feature, layer_namespace CASCADE;`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// ancestry contains all meta information around scanned manifest and its
|
||||||
|
// layers.
|
||||||
|
ancestry = MigrationQuery{
|
||||||
|
Up: []string{
|
||||||
// ancestry
|
// ancestry
|
||||||
`CREATE TABLE IF NOT EXISTS ancestry (
|
`CREATE TABLE IF NOT EXISTS ancestry (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
@ -93,28 +124,31 @@ func init() {
|
|||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
ancestry_layer_id INT REFERENCES ancestry_layer ON DELETE CASCADE,
|
ancestry_layer_id INT REFERENCES ancestry_layer ON DELETE CASCADE,
|
||||||
namespaced_feature_id INT REFERENCES namespaced_feature ON DELETE CASCADE,
|
namespaced_feature_id INT REFERENCES namespaced_feature ON DELETE CASCADE,
|
||||||
|
feature_detector_id INT REFERENCES detector ON DELETE CASCADE,
|
||||||
|
namespace_detector_id INT REFERENCES detector ON DELETE CASCADE,
|
||||||
UNIQUE (ancestry_layer_id, namespaced_feature_id));`,
|
UNIQUE (ancestry_layer_id, namespaced_feature_id));`,
|
||||||
|
|
||||||
`CREATE TABLE IF NOT EXISTS ancestry_lister (
|
`CREATE TABLE IF NOT EXISTS ancestry_detector(
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
ancestry_id INT REFERENCES ancestry ON DELETE CASCADE,
|
ancestry_id INT REFERENCES layer ON DELETE CASCADE,
|
||||||
lister TEXT,
|
detector_id INT REFERENCES detector ON DELETE CASCADE,
|
||||||
UNIQUE (ancestry_id, lister));`,
|
UNIQUE(ancestry_id, detector_id));`,
|
||||||
`CREATE INDEX ON ancestry_lister(ancestry_id);`,
|
|
||||||
|
|
||||||
`CREATE TABLE IF NOT EXISTS ancestry_detector (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
ancestry_id INT REFERENCES ancestry ON DELETE CASCADE,
|
|
||||||
detector TEXT,
|
|
||||||
UNIQUE (ancestry_id, detector));`,
|
|
||||||
`CREATE INDEX ON ancestry_detector(ancestry_id);`,
|
`CREATE INDEX ON ancestry_detector(ancestry_id);`,
|
||||||
|
},
|
||||||
|
Down: []string{
|
||||||
|
`DROP TABLE IF EXISTS ancestry, ancestry_layer, ancestry_feature, ancestry_detector CASCADE;`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// vulnerability contains the metadata and vulnerability affecting relation.
|
||||||
|
vulnerability = MigrationQuery{
|
||||||
|
Up: []string{
|
||||||
`CREATE TYPE severity AS ENUM ('Unknown', 'Negligible', 'Low', 'Medium', 'High', 'Critical', 'Defcon1');`,
|
`CREATE TYPE severity AS ENUM ('Unknown', 'Negligible', 'Low', 'Medium', 'High', 'Critical', 'Defcon1');`,
|
||||||
|
|
||||||
// vulnerability
|
// vulnerability
|
||||||
`CREATE TABLE IF NOT EXISTS vulnerability (
|
`CREATE TABLE IF NOT EXISTS vulnerability (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
namespace_id INT NOT NULL REFERENCES Namespace,
|
namespace_id INT REFERENCES Namespace,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
description TEXT NULL,
|
description TEXT NULL,
|
||||||
link TEXT NULL,
|
link TEXT NULL,
|
||||||
@ -127,7 +161,7 @@ func init() {
|
|||||||
|
|
||||||
`CREATE TABLE IF NOT EXISTS vulnerability_affected_feature (
|
`CREATE TABLE IF NOT EXISTS vulnerability_affected_feature (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
vulnerability_id INT NOT NULL REFERENCES vulnerability ON DELETE CASCADE,
|
vulnerability_id INT REFERENCES vulnerability ON DELETE CASCADE,
|
||||||
feature_name TEXT NOT NULL,
|
feature_name TEXT NOT NULL,
|
||||||
affected_version TEXT,
|
affected_version TEXT,
|
||||||
fixedin TEXT);`,
|
fixedin TEXT);`,
|
||||||
@ -135,12 +169,22 @@ func init() {
|
|||||||
|
|
||||||
`CREATE TABLE IF NOT EXISTS vulnerability_affected_namespaced_feature(
|
`CREATE TABLE IF NOT EXISTS vulnerability_affected_namespaced_feature(
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
vulnerability_id INT NOT NULL REFERENCES vulnerability ON DELETE CASCADE,
|
vulnerability_id INT REFERENCES vulnerability ON DELETE CASCADE,
|
||||||
namespaced_feature_id INT NOT NULL REFERENCES namespaced_feature ON DELETE CASCADE,
|
namespaced_feature_id INT REFERENCES namespaced_feature ON DELETE CASCADE,
|
||||||
added_by INT NOT NULL REFERENCES vulnerability_affected_feature ON DELETE CASCADE,
|
added_by INT REFERENCES vulnerability_affected_feature ON DELETE CASCADE,
|
||||||
UNIQUE (vulnerability_id, namespaced_feature_id));`,
|
UNIQUE (vulnerability_id, namespaced_feature_id));`,
|
||||||
`CREATE INDEX ON vulnerability_affected_namespaced_feature(namespaced_feature_id);`,
|
`CREATE INDEX ON vulnerability_affected_namespaced_feature(namespaced_feature_id);`,
|
||||||
|
},
|
||||||
|
Down: []string{
|
||||||
|
`DROP TYPE IF EXISTS severity;`,
|
||||||
|
`DROP TABLE IF EXISTS vulnerability, vulnerability_affected_feature, vulnerability_affected_namespaced_feature CASCADE;`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// updaterLock is the lock to be used by updater to prevent multiple
|
||||||
|
// updaters running on the same vulnerability source.
|
||||||
|
updaterLock = MigrationQuery{
|
||||||
|
Up: []string{
|
||||||
`CREATE TABLE IF NOT EXISTS KeyValue (
|
`CREATE TABLE IF NOT EXISTS KeyValue (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
key TEXT NOT NULL UNIQUE,
|
key TEXT NOT NULL UNIQUE,
|
||||||
@ -152,8 +196,16 @@ func init() {
|
|||||||
owner VARCHAR(64) NOT NULL,
|
owner VARCHAR(64) NOT NULL,
|
||||||
until TIMESTAMP WITH TIME ZONE);`,
|
until TIMESTAMP WITH TIME ZONE);`,
|
||||||
`CREATE INDEX ON Lock (owner);`,
|
`CREATE INDEX ON Lock (owner);`,
|
||||||
|
},
|
||||||
|
Down: []string{
|
||||||
|
`DROP TABLE IF EXISTS KeyValue, Lock CASCADE;`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// Notification
|
// notification is the vulnerability notification spawned by the
|
||||||
|
// vulnerability changes.
|
||||||
|
notification = MigrationQuery{
|
||||||
|
Up: []string{
|
||||||
`CREATE TABLE IF NOT EXISTS Vulnerability_Notification (
|
`CREATE TABLE IF NOT EXISTS Vulnerability_Notification (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
name VARCHAR(64) NOT NULL UNIQUE,
|
name VARCHAR(64) NOT NULL UNIQUE,
|
||||||
@ -163,30 +215,22 @@ func init() {
|
|||||||
old_vulnerability_id INT NULL REFERENCES Vulnerability ON DELETE CASCADE,
|
old_vulnerability_id INT NULL REFERENCES Vulnerability ON DELETE CASCADE,
|
||||||
new_vulnerability_id INT NULL REFERENCES Vulnerability ON DELETE CASCADE);`,
|
new_vulnerability_id INT NULL REFERENCES Vulnerability ON DELETE CASCADE);`,
|
||||||
`CREATE INDEX ON Vulnerability_Notification (notified_at);`,
|
`CREATE INDEX ON Vulnerability_Notification (notified_at);`,
|
||||||
}),
|
},
|
||||||
Down: migrate.Queries([]string{
|
Down: []string{
|
||||||
`DROP TABLE IF EXISTS
|
`DROP TABLE IF EXISTS Vulnerability_Notification CASCADE;`,
|
||||||
ancestry,
|
},
|
||||||
ancestry_layer,
|
}
|
||||||
ancestry_detector,
|
)
|
||||||
ancestry_lister,
|
|
||||||
ancestry_feature,
|
func init() {
|
||||||
feature,
|
RegisterMigration(NewSimpleMigration(1,
|
||||||
namespaced_feature,
|
[]MigrationQuery{
|
||||||
keyvalue,
|
entities,
|
||||||
layer,
|
detector,
|
||||||
layer_detector,
|
layer,
|
||||||
layer_feature,
|
ancestry,
|
||||||
layer_lister,
|
vulnerability,
|
||||||
layer_namespace,
|
updaterLock,
|
||||||
lock,
|
notification,
|
||||||
namespace,
|
}))
|
||||||
vulnerability,
|
|
||||||
vulnerability_affected_feature,
|
|
||||||
vulnerability_affected_namespaced_feature,
|
|
||||||
vulnerability_notification
|
|
||||||
CASCADE;`,
|
|
||||||
`DROP TYPE IF EXISTS severity;`,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
30
database/pgsql/migrations/util.go
Normal file
30
database/pgsql/migrations/util.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import "github.com/remind101/migrate"
|
||||||
|
|
||||||
|
// MigrationQuery contains the Up migration and Down migration in Plain strings.
|
||||||
|
type MigrationQuery struct {
|
||||||
|
Up []string
|
||||||
|
Down []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConcatMigrationQueries concats migration queries in the give order.
|
||||||
|
func ConcatMigrationQueries(qs []MigrationQuery) MigrationQuery {
|
||||||
|
r := MigrationQuery{}
|
||||||
|
for _, q := range qs {
|
||||||
|
r.Up = append(r.Up, q.Up...)
|
||||||
|
r.Down = append(r.Down, q.Down...)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSimpleMigration returns a simple migration plan with all provided
|
||||||
|
// migration queries concatted in order.
|
||||||
|
func NewSimpleMigration(id int, qs []MigrationQuery) migrate.Migration {
|
||||||
|
q := ConcatMigrationQueries(qs)
|
||||||
|
return migrate.Migration{
|
||||||
|
ID: id,
|
||||||
|
Up: migrate.Queries(q.Up),
|
||||||
|
Down: migrate.Queries(q.Down),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user