@absolutejs/vulnerabilities-postgres 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/index.d.ts +4 -1
- package/dist/index.js +258 -3
- package/dist/manifest.js +4 -4
- package/dist/manifest.json +4 -4
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -14,11 +14,11 @@ const osvSnapshots = persistence.snapshots();
|
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
The package stores complete provider snapshots and records, appendable sync
|
|
17
|
-
history, tenant-scoped managed findings
|
|
18
|
-
expiring distributed refresh leases. Snapshot replacement is
|
|
19
|
-
statement, so readers never see a half-replaced record set. Schema
|
|
20
|
-
lazy and idempotent, or can be disabled when application migrations
|
|
21
|
-
tables.
|
|
17
|
+
history, tenant-scoped managed findings, correlation observations, VEX decisions
|
|
18
|
+
and applications, risk assessments, and expiring distributed refresh leases. Snapshot replacement is
|
|
19
|
+
one Postgres statement, so readers never see a half-replaced record set. Schema
|
|
20
|
+
creation is lazy and idempotent, or can be disabled when application migrations
|
|
21
|
+
own the tables.
|
|
22
22
|
|
|
23
23
|
The SQL surface is compatible with postgres.js and Neon-style tagged-template
|
|
24
24
|
clients. Table prefixes are strictly validated before identifiers are included
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type FeedSnapshotStore, type FeedSyncRunStore, type ManagedFindingStore, type VulnerabilityObservationStore } from "@absolutejs/vulnerabilities";
|
|
1
|
+
import { type FeedSnapshotStore, type FeedSyncRunStore, type ManagedFindingStore, type VulnerabilityObservationStore, type VulnerabilityRiskAssessmentStore, type VexDecisionStore, type VexFindingApplicationStore } from "@absolutejs/vulnerabilities";
|
|
2
2
|
export type PostgresTag = {
|
|
3
3
|
(strings: TemplateStringsArray, ...values: never[]): PromiseLike<unknown[]>;
|
|
4
4
|
unsafe: (sql: string, ...args: never[]) => PromiseLike<unknown[]>;
|
|
@@ -18,8 +18,11 @@ export type PostgresVulnerabilityStore = {
|
|
|
18
18
|
findings: ManagedFindingStore;
|
|
19
19
|
leases: FeedLeaseStore;
|
|
20
20
|
observations: VulnerabilityObservationStore;
|
|
21
|
+
riskAssessments: VulnerabilityRiskAssessmentStore;
|
|
21
22
|
snapshots: <T>() => FeedSnapshotStore<T>;
|
|
22
23
|
syncRuns: FeedSyncRunStore;
|
|
24
|
+
vexApplications: VexFindingApplicationStore;
|
|
25
|
+
vexDecisions: VexDecisionStore;
|
|
23
26
|
};
|
|
24
27
|
export type CreatePostgresVulnerabilityStoreOptions = {
|
|
25
28
|
ensureSchema?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
// src/index.ts
|
|
3
3
|
import {
|
|
4
4
|
ManagedVulnerabilityFindingSchema,
|
|
5
|
-
VulnerabilityObservationSchema
|
|
5
|
+
VulnerabilityObservationSchema,
|
|
6
|
+
VulnerabilityRiskAssessmentSchema,
|
|
7
|
+
VexDecisionSchema,
|
|
8
|
+
VexFindingApplicationSchema
|
|
6
9
|
} from "@absolutejs/vulnerabilities";
|
|
7
10
|
import { Value } from "@sinclair/typebox/value";
|
|
8
11
|
var IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
@@ -58,9 +61,12 @@ var prefixTables = (prefix) => ({
|
|
|
58
61
|
findings: `${prefix}_findings`,
|
|
59
62
|
leases: `${prefix}_feed_leases`,
|
|
60
63
|
observations: `${prefix}_observations`,
|
|
64
|
+
riskAssessments: `${prefix}_risk_assessments`,
|
|
61
65
|
records: `${prefix}_feed_records`,
|
|
62
66
|
runs: `${prefix}_feed_sync_runs`,
|
|
63
|
-
snapshots: `${prefix}_feed_snapshots
|
|
67
|
+
snapshots: `${prefix}_feed_snapshots`,
|
|
68
|
+
vexApplications: `${prefix}_vex_applications`,
|
|
69
|
+
vexDecisions: `${prefix}_vex_decisions`
|
|
64
70
|
});
|
|
65
71
|
var vulnerabilityPostgresSchemaSql = (tablePrefix = "vulnerability") => {
|
|
66
72
|
if (!IDENTIFIER.test(tablePrefix))
|
|
@@ -132,6 +138,49 @@ var vulnerabilityPostgresSchemaSql = (tablePrefix = "vulnerability") => {
|
|
|
132
138
|
ON ${table.observations} (tenant_id, asset_id, observed_at DESC);
|
|
133
139
|
CREATE INDEX IF NOT EXISTS ${table.observations}_tenant_component_idx
|
|
134
140
|
ON ${table.observations} (tenant_id, component_id, observed_at DESC);
|
|
141
|
+
CREATE TABLE IF NOT EXISTS ${table.riskAssessments} (
|
|
142
|
+
tenant_id text NOT NULL,
|
|
143
|
+
finding_id text NOT NULL,
|
|
144
|
+
priority text NOT NULL,
|
|
145
|
+
assessed_at timestamptz NOT NULL,
|
|
146
|
+
remediate_by timestamptz,
|
|
147
|
+
value jsonb NOT NULL,
|
|
148
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
149
|
+
PRIMARY KEY (tenant_id, finding_id)
|
|
150
|
+
);
|
|
151
|
+
CREATE INDEX IF NOT EXISTS ${table.riskAssessments}_tenant_priority_idx
|
|
152
|
+
ON ${table.riskAssessments} (tenant_id, priority, assessed_at DESC);
|
|
153
|
+
CREATE INDEX IF NOT EXISTS ${table.riskAssessments}_tenant_due_idx
|
|
154
|
+
ON ${table.riskAssessments} (tenant_id, remediate_by)
|
|
155
|
+
WHERE remediate_by IS NOT NULL;
|
|
156
|
+
CREATE TABLE IF NOT EXISTS ${table.vexDecisions} (
|
|
157
|
+
tenant_id text NOT NULL,
|
|
158
|
+
decision_id text NOT NULL,
|
|
159
|
+
product_id text NOT NULL,
|
|
160
|
+
vulnerability_id text NOT NULL,
|
|
161
|
+
status text NOT NULL,
|
|
162
|
+
created_at timestamptz NOT NULL,
|
|
163
|
+
expires_at timestamptz,
|
|
164
|
+
value jsonb NOT NULL,
|
|
165
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
166
|
+
PRIMARY KEY (tenant_id, decision_id)
|
|
167
|
+
);
|
|
168
|
+
CREATE INDEX IF NOT EXISTS ${table.vexDecisions}_tenant_product_idx
|
|
169
|
+
ON ${table.vexDecisions} (tenant_id, product_id, created_at DESC);
|
|
170
|
+
CREATE INDEX IF NOT EXISTS ${table.vexDecisions}_tenant_vulnerability_idx
|
|
171
|
+
ON ${table.vexDecisions} (tenant_id, vulnerability_id, created_at DESC);
|
|
172
|
+
CREATE TABLE IF NOT EXISTS ${table.vexApplications} (
|
|
173
|
+
tenant_id text NOT NULL,
|
|
174
|
+
finding_id text NOT NULL,
|
|
175
|
+
decision_id text NOT NULL,
|
|
176
|
+
applied_at timestamptz NOT NULL,
|
|
177
|
+
ended_at timestamptz,
|
|
178
|
+
value jsonb NOT NULL,
|
|
179
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
180
|
+
PRIMARY KEY (tenant_id, finding_id)
|
|
181
|
+
);
|
|
182
|
+
CREATE INDEX IF NOT EXISTS ${table.vexApplications}_tenant_decision_idx
|
|
183
|
+
ON ${table.vexApplications} (tenant_id, decision_id, applied_at DESC);
|
|
135
184
|
CREATE TABLE IF NOT EXISTS ${table.leases} (
|
|
136
185
|
feed_id text PRIMARY KEY,
|
|
137
186
|
owner_id text NOT NULL,
|
|
@@ -465,6 +514,209 @@ var createPostgresVulnerabilityStore = (options) => {
|
|
|
465
514
|
save: async (tenantId, observation) => saveObservations(tenantId, [observation]),
|
|
466
515
|
saveMany: saveObservations
|
|
467
516
|
};
|
|
517
|
+
const parseRiskAssessment = (value) => {
|
|
518
|
+
const assessment = json(value, "Vulnerability risk assessment");
|
|
519
|
+
if (!Value.Check(VulnerabilityRiskAssessmentSchema, assessment))
|
|
520
|
+
throw new Error("Stored vulnerability risk assessment is invalid");
|
|
521
|
+
return assessment;
|
|
522
|
+
};
|
|
523
|
+
const saveRiskAssessments = async (tenantId, assessments) => {
|
|
524
|
+
await ensureSchema();
|
|
525
|
+
const normalizedTenantId = requiredText(tenantId, "Tenant id");
|
|
526
|
+
if (assessments.length === 0)
|
|
527
|
+
return;
|
|
528
|
+
for (const assessment of assessments) {
|
|
529
|
+
const findingId = assessment.findingId;
|
|
530
|
+
if (!Value.Check(VulnerabilityRiskAssessmentSchema, assessment))
|
|
531
|
+
throw new Error(`Vulnerability risk assessment ${findingId} is invalid`);
|
|
532
|
+
}
|
|
533
|
+
const payload = JSON.stringify(assessments.map((assessment) => ({
|
|
534
|
+
assessed_at: assessment.assessedAt,
|
|
535
|
+
finding_id: assessment.findingId,
|
|
536
|
+
priority: assessment.priority,
|
|
537
|
+
remediate_by: assessment.remediateBy,
|
|
538
|
+
value: assessment
|
|
539
|
+
})));
|
|
540
|
+
await sql`
|
|
541
|
+
INSERT INTO ${sql.unsafe(table.riskAssessments)} (
|
|
542
|
+
tenant_id, finding_id, priority, assessed_at, remediate_by, value,
|
|
543
|
+
updated_at
|
|
544
|
+
)
|
|
545
|
+
SELECT
|
|
546
|
+
${normalizedTenantId}, item.finding_id, item.priority,
|
|
547
|
+
item.assessed_at::timestamptz, item.remediate_by::timestamptz,
|
|
548
|
+
item.value, now()
|
|
549
|
+
FROM jsonb_to_recordset(${payload}::jsonb) AS item(
|
|
550
|
+
finding_id text, priority text, assessed_at text, remediate_by text,
|
|
551
|
+
value jsonb
|
|
552
|
+
)
|
|
553
|
+
ON CONFLICT (tenant_id, finding_id) DO UPDATE SET
|
|
554
|
+
priority = EXCLUDED.priority,
|
|
555
|
+
assessed_at = EXCLUDED.assessed_at,
|
|
556
|
+
remediate_by = EXCLUDED.remediate_by,
|
|
557
|
+
value = EXCLUDED.value,
|
|
558
|
+
updated_at = now()
|
|
559
|
+
`;
|
|
560
|
+
};
|
|
561
|
+
const riskAssessments = {
|
|
562
|
+
get: async (tenantId, findingId) => {
|
|
563
|
+
await ensureSchema();
|
|
564
|
+
const rows = await sql`
|
|
565
|
+
SELECT value FROM ${sql.unsafe(table.riskAssessments)}
|
|
566
|
+
WHERE tenant_id = ${requiredText(tenantId, "Tenant id")}
|
|
567
|
+
AND finding_id = ${requiredText(findingId, "Finding id")}
|
|
568
|
+
`;
|
|
569
|
+
return rows[0] ? parseRiskAssessment(rows[0].value) : null;
|
|
570
|
+
},
|
|
571
|
+
list: async (filter) => {
|
|
572
|
+
await ensureSchema();
|
|
573
|
+
const tenantId = requiredText(filter.tenantId, "Tenant id");
|
|
574
|
+
const priority = filter.priority ?? null;
|
|
575
|
+
const rows = await sql`
|
|
576
|
+
SELECT value FROM ${sql.unsafe(table.riskAssessments)}
|
|
577
|
+
WHERE tenant_id = ${tenantId}
|
|
578
|
+
AND (${priority}::text IS NULL OR priority = ${priority})
|
|
579
|
+
ORDER BY assessed_at DESC, finding_id
|
|
580
|
+
LIMIT ${limit(filter.limit)}
|
|
581
|
+
`;
|
|
582
|
+
return rows.map(({ value }) => parseRiskAssessment(value));
|
|
583
|
+
},
|
|
584
|
+
save: async (tenantId, assessment) => saveRiskAssessments(tenantId, [assessment]),
|
|
585
|
+
saveMany: saveRiskAssessments
|
|
586
|
+
};
|
|
587
|
+
const parseVexDecision = (value) => {
|
|
588
|
+
const decision = json(value, "VEX decision");
|
|
589
|
+
if (!Value.Check(VexDecisionSchema, decision))
|
|
590
|
+
throw new Error("Stored VEX decision is invalid");
|
|
591
|
+
return decision;
|
|
592
|
+
};
|
|
593
|
+
const saveVexDecisions = async (tenantId, decisions) => {
|
|
594
|
+
await ensureSchema();
|
|
595
|
+
const normalizedTenantId = requiredText(tenantId, "Tenant id");
|
|
596
|
+
if (decisions.length === 0)
|
|
597
|
+
return;
|
|
598
|
+
for (const decision of decisions) {
|
|
599
|
+
const decisionId = decision.id;
|
|
600
|
+
if (!Value.Check(VexDecisionSchema, decision))
|
|
601
|
+
throw new Error(`VEX decision ${decisionId} is invalid`);
|
|
602
|
+
}
|
|
603
|
+
const payload = JSON.stringify(decisions.map((decision) => ({
|
|
604
|
+
created_at: decision.createdAt,
|
|
605
|
+
decision_id: decision.id,
|
|
606
|
+
expires_at: decision.expiresAt,
|
|
607
|
+
product_id: decision.productId,
|
|
608
|
+
status: decision.status,
|
|
609
|
+
value: decision,
|
|
610
|
+
vulnerability_id: decision.vulnerabilityId
|
|
611
|
+
})));
|
|
612
|
+
await sql`
|
|
613
|
+
INSERT INTO ${sql.unsafe(table.vexDecisions)} (
|
|
614
|
+
tenant_id, decision_id, product_id, vulnerability_id, status,
|
|
615
|
+
created_at, expires_at, value, updated_at
|
|
616
|
+
)
|
|
617
|
+
SELECT
|
|
618
|
+
${normalizedTenantId}, item.decision_id, item.product_id,
|
|
619
|
+
item.vulnerability_id, item.status, item.created_at::timestamptz,
|
|
620
|
+
item.expires_at::timestamptz, item.value, now()
|
|
621
|
+
FROM jsonb_to_recordset(${payload}::jsonb) AS item(
|
|
622
|
+
decision_id text, product_id text, vulnerability_id text, status text,
|
|
623
|
+
created_at text, expires_at text, value jsonb
|
|
624
|
+
)
|
|
625
|
+
ON CONFLICT (tenant_id, decision_id) DO UPDATE SET
|
|
626
|
+
product_id = EXCLUDED.product_id,
|
|
627
|
+
vulnerability_id = EXCLUDED.vulnerability_id,
|
|
628
|
+
status = EXCLUDED.status,
|
|
629
|
+
created_at = EXCLUDED.created_at,
|
|
630
|
+
expires_at = EXCLUDED.expires_at,
|
|
631
|
+
value = EXCLUDED.value,
|
|
632
|
+
updated_at = now()
|
|
633
|
+
`;
|
|
634
|
+
};
|
|
635
|
+
const vexDecisions = {
|
|
636
|
+
get: async (tenantId, decisionId) => {
|
|
637
|
+
await ensureSchema();
|
|
638
|
+
const rows = await sql`
|
|
639
|
+
SELECT value FROM ${sql.unsafe(table.vexDecisions)}
|
|
640
|
+
WHERE tenant_id = ${requiredText(tenantId, "Tenant id")}
|
|
641
|
+
AND decision_id = ${requiredText(decisionId, "Decision id")}
|
|
642
|
+
`;
|
|
643
|
+
return rows[0] ? parseVexDecision(rows[0].value) : null;
|
|
644
|
+
},
|
|
645
|
+
list: async (filter) => {
|
|
646
|
+
await ensureSchema();
|
|
647
|
+
const tenantId = requiredText(filter.tenantId, "Tenant id");
|
|
648
|
+
const productId = filter.productId?.trim() || null;
|
|
649
|
+
const vulnerabilityId = filter.vulnerabilityId?.trim() || null;
|
|
650
|
+
const rows = await sql`
|
|
651
|
+
SELECT value FROM ${sql.unsafe(table.vexDecisions)}
|
|
652
|
+
WHERE tenant_id = ${tenantId}
|
|
653
|
+
AND (${productId}::text IS NULL OR product_id = ${productId})
|
|
654
|
+
AND (${vulnerabilityId}::text IS NULL OR vulnerability_id = ${vulnerabilityId})
|
|
655
|
+
ORDER BY created_at DESC, decision_id
|
|
656
|
+
LIMIT ${limit(filter.limit)}
|
|
657
|
+
`;
|
|
658
|
+
return rows.map(({ value }) => parseVexDecision(value));
|
|
659
|
+
},
|
|
660
|
+
save: async (tenantId, decision) => saveVexDecisions(tenantId, [decision]),
|
|
661
|
+
saveMany: saveVexDecisions
|
|
662
|
+
};
|
|
663
|
+
const parseVexApplication = (value) => {
|
|
664
|
+
const application = json(value, "VEX application");
|
|
665
|
+
if (!Value.Check(VexFindingApplicationSchema, application))
|
|
666
|
+
throw new Error("Stored VEX application is invalid");
|
|
667
|
+
return application;
|
|
668
|
+
};
|
|
669
|
+
const saveVexApplications = async (applications) => {
|
|
670
|
+
await ensureSchema();
|
|
671
|
+
if (applications.length === 0)
|
|
672
|
+
return;
|
|
673
|
+
for (const application of applications) {
|
|
674
|
+
const findingId = application.findingId;
|
|
675
|
+
if (!Value.Check(VexFindingApplicationSchema, application))
|
|
676
|
+
throw new Error(`VEX application ${findingId} is invalid`);
|
|
677
|
+
}
|
|
678
|
+
const payload = JSON.stringify(applications.map((application) => ({
|
|
679
|
+
applied_at: application.appliedAt,
|
|
680
|
+
decision_id: application.decisionId,
|
|
681
|
+
ended_at: application.endedAt,
|
|
682
|
+
finding_id: application.findingId,
|
|
683
|
+
tenant_id: application.tenantId,
|
|
684
|
+
value: application
|
|
685
|
+
})));
|
|
686
|
+
await sql`
|
|
687
|
+
INSERT INTO ${sql.unsafe(table.vexApplications)} (
|
|
688
|
+
tenant_id, finding_id, decision_id, applied_at, ended_at, value,
|
|
689
|
+
updated_at
|
|
690
|
+
)
|
|
691
|
+
SELECT
|
|
692
|
+
item.tenant_id, item.finding_id, item.decision_id,
|
|
693
|
+
item.applied_at::timestamptz, item.ended_at::timestamptz,
|
|
694
|
+
item.value, now()
|
|
695
|
+
FROM jsonb_to_recordset(${payload}::jsonb) AS item(
|
|
696
|
+
tenant_id text, finding_id text, decision_id text, applied_at text,
|
|
697
|
+
ended_at text, value jsonb
|
|
698
|
+
)
|
|
699
|
+
ON CONFLICT (tenant_id, finding_id) DO UPDATE SET
|
|
700
|
+
decision_id = EXCLUDED.decision_id,
|
|
701
|
+
applied_at = EXCLUDED.applied_at,
|
|
702
|
+
ended_at = EXCLUDED.ended_at,
|
|
703
|
+
value = EXCLUDED.value,
|
|
704
|
+
updated_at = now()
|
|
705
|
+
`;
|
|
706
|
+
};
|
|
707
|
+
const vexApplications = {
|
|
708
|
+
get: async (tenantId, findingId) => {
|
|
709
|
+
await ensureSchema();
|
|
710
|
+
const rows = await sql`
|
|
711
|
+
SELECT value FROM ${sql.unsafe(table.vexApplications)}
|
|
712
|
+
WHERE tenant_id = ${requiredText(tenantId, "Tenant id")}
|
|
713
|
+
AND finding_id = ${requiredText(findingId, "Finding id")}
|
|
714
|
+
`;
|
|
715
|
+
return rows[0] ? parseVexApplication(rows[0].value) : null;
|
|
716
|
+
},
|
|
717
|
+
save: async (application) => saveVexApplications([application]),
|
|
718
|
+
saveMany: saveVexApplications
|
|
719
|
+
};
|
|
468
720
|
const leases = {
|
|
469
721
|
acquire: async (request) => {
|
|
470
722
|
await ensureSchema();
|
|
@@ -507,8 +759,11 @@ var createPostgresVulnerabilityStore = (options) => {
|
|
|
507
759
|
findings,
|
|
508
760
|
leases,
|
|
509
761
|
observations,
|
|
762
|
+
riskAssessments,
|
|
510
763
|
snapshots,
|
|
511
|
-
syncRuns
|
|
764
|
+
syncRuns,
|
|
765
|
+
vexApplications,
|
|
766
|
+
vexDecisions
|
|
512
767
|
};
|
|
513
768
|
};
|
|
514
769
|
export {
|
package/dist/manifest.js
CHANGED
|
@@ -9,7 +9,7 @@ var manifest = defineManifest()({
|
|
|
9
9
|
intents: [
|
|
10
10
|
"persist vulnerability intelligence",
|
|
11
11
|
"retain vulnerability sync history",
|
|
12
|
-
"store managed vulnerability findings and
|
|
12
|
+
"store managed vulnerability findings, observations, VEX decisions, and risk assessments"
|
|
13
13
|
],
|
|
14
14
|
keywords: ["vulnerabilities", "Postgres", "CVE", "feed-history"],
|
|
15
15
|
protocols: ["PostgreSQL", "AbsoluteJS vulnerability contracts"]
|
|
@@ -17,7 +17,7 @@ var manifest = defineManifest()({
|
|
|
17
17
|
identity: {
|
|
18
18
|
accent: "#336791",
|
|
19
19
|
category: "operations",
|
|
20
|
-
description: "Durable Postgres snapshots,
|
|
20
|
+
description: "Durable Postgres snapshots, sync history, findings, observations, VEX decisions, risk assessments, and distributed refresh leases.",
|
|
21
21
|
docsUrl: "https://github.com/absolutejs/vulnerabilities-postgres",
|
|
22
22
|
name: "@absolutejs/vulnerabilities-postgres",
|
|
23
23
|
tagline: "Keep vulnerability intelligence durable and auditable."
|
|
@@ -30,7 +30,7 @@ var manifest = defineManifest()({
|
|
|
30
30
|
requires: {
|
|
31
31
|
env: [
|
|
32
32
|
{
|
|
33
|
-
description: "Postgres connection string for vulnerability intelligence, findings, and
|
|
33
|
+
description: "Postgres connection string for vulnerability intelligence, findings, observations, VEX decisions, and risk assessments",
|
|
34
34
|
example: "postgres://user:pass@host/db",
|
|
35
35
|
key: "DATABASE_URL",
|
|
36
36
|
secret: true
|
|
@@ -45,7 +45,7 @@ var manifest = defineManifest()({
|
|
|
45
45
|
],
|
|
46
46
|
services: [
|
|
47
47
|
{
|
|
48
|
-
description: "Stores vulnerability intelligence,
|
|
48
|
+
description: "Stores vulnerability intelligence, findings, observations, VEX decisions, risk assessments, and leases",
|
|
49
49
|
id: "postgres"
|
|
50
50
|
}
|
|
51
51
|
]
|
package/dist/manifest.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"intents": [
|
|
9
9
|
"persist vulnerability intelligence",
|
|
10
10
|
"retain vulnerability sync history",
|
|
11
|
-
"store managed vulnerability findings and
|
|
11
|
+
"store managed vulnerability findings, observations, VEX decisions, and risk assessments"
|
|
12
12
|
],
|
|
13
13
|
"keywords": [
|
|
14
14
|
"vulnerabilities",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"identity": {
|
|
25
25
|
"accent": "#336791",
|
|
26
26
|
"category": "operations",
|
|
27
|
-
"description": "Durable Postgres snapshots,
|
|
27
|
+
"description": "Durable Postgres snapshots, sync history, findings, observations, VEX decisions, risk assessments, and distributed refresh leases.",
|
|
28
28
|
"docsUrl": "https://github.com/absolutejs/vulnerabilities-postgres",
|
|
29
29
|
"name": "@absolutejs/vulnerabilities-postgres",
|
|
30
30
|
"tagline": "Keep vulnerability intelligence durable and auditable."
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"requires": {
|
|
38
38
|
"env": [
|
|
39
39
|
{
|
|
40
|
-
"description": "Postgres connection string for vulnerability intelligence, findings, and
|
|
40
|
+
"description": "Postgres connection string for vulnerability intelligence, findings, observations, VEX decisions, and risk assessments",
|
|
41
41
|
"example": "postgres://user:pass@host/db",
|
|
42
42
|
"key": "DATABASE_URL",
|
|
43
43
|
"secret": true
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
],
|
|
53
53
|
"services": [
|
|
54
54
|
{
|
|
55
|
-
"description": "Stores vulnerability intelligence,
|
|
55
|
+
"description": "Stores vulnerability intelligence, findings, observations, VEX decisions, risk assessments, and leases",
|
|
56
56
|
"id": "postgres"
|
|
57
57
|
}
|
|
58
58
|
]
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@absolutejs/vulnerabilities-postgres",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Durable Postgres
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Durable Postgres feeds, findings, observations, VEX decisions, risk assessments, and refresh leases for AbsoluteJS vulnerability management.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BSL-1.1",
|
|
7
7
|
"author": "Alex Kahn",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
],
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@absolutejs/manifest": "0.3.0",
|
|
56
|
-
"@absolutejs/vulnerabilities": "0.
|
|
56
|
+
"@absolutejs/vulnerabilities": "0.7.1",
|
|
57
57
|
"@sinclair/typebox": "0.34.52"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|