SSVL: add timestamp-based check for the exclude-any case (#24889)

Co-authored-by: Jahziel Villasana-Espinoza <jahziel@fleetdm.com>
This commit is contained in:
Martin Angers
2024-12-19 11:03:44 -06:00
committed by GitHub
co-authored by Jahziel Villasana-Espinoza
parent 89862b012b
commit b4516df1d3
3 changed files with 81 additions and 23 deletions
+18 -10
View File
@@ -2386,7 +2386,7 @@ INNER JOIN software_cve scve ON scve.software_id = s.id
SELECT 1 FROM (
-- no labels
SELECT 0 AS count_installer_labels, 0 AS count_host_labels
SELECT 0 AS count_installer_labels, 0 AS count_host_labels, 0 as count_host_updated_after_labels
WHERE NOT EXISTS (
SELECT 1 FROM software_installer_labels sil WHERE sil.software_installer_id = si.id
)
@@ -2396,7 +2396,8 @@ INNER JOIN software_cve scve ON scve.software_id = s.id
-- include any
SELECT
COUNT(*) AS count_installer_labels,
COUNT(lm.label_id) AS count_host_labels
COUNT(lm.label_id) AS count_host_labels,
0 as count_host_updated_after_labels
FROM
software_installer_labels sil
LEFT OUTER JOIN label_membership lm ON lm.label_id = sil.label_id
@@ -2405,27 +2406,33 @@ INNER JOIN software_cve scve ON scve.software_id = s.id
sil.software_installer_id = si.id
AND sil.exclude = 0
HAVING
count_installer_labels > 0 AND count_host_labels > 0
count_installer_labels > 0 AND count_host_labels > 0
UNION
-- exclude any
-- exclude any, ignore software that depends on labels created
-- _after_ the label_updated_at timestamp of the host (because
-- we don't have results for that label yet, the host may or may
-- not be a member).
SELECT
COUNT(*) AS count_installer_labels,
COUNT(lm.label_id) AS count_host_labels
COUNT(lm.label_id) AS count_host_labels,
SUM(CASE WHEN lbl.created_at IS NOT NULL AND :host_label_updated_at >= lbl.created_at THEN 1 ELSE 0 END) as count_host_updated_after_labels
FROM
software_installer_labels sil
LEFT OUTER JOIN label_membership lm ON lm.label_id = sil.label_id
AND lm.host_id = :host_id
LEFT OUTER JOIN labels lbl
ON lbl.id = sil.label_id
LEFT OUTER JOIN label_membership lm
ON lm.label_id = sil.label_id AND lm.host_id = :host_id
WHERE
sil.software_installer_id = si.id
AND sil.exclude = 1
HAVING
count_installer_labels > 0 AND count_host_labels = 0
count_installer_labels > 0 AND count_installer_labels = count_host_updated_after_labels AND count_host_labels = 0
) t
)
)
-- it's some other type of software that has been checked above
-- it's some other type of software that has been checked above
ELSE true END
)
%s %s
@@ -2472,6 +2479,7 @@ INNER JOIN software_cve scve ON scve.software_id = s.id
stmt := stmtInstalled
if opts.OnlyAvailableForInstall || (opts.IncludeAvailableForInstall && !opts.VulnerableOnly) {
namedArgs["vpp_apps_platforms"] = fleet.VPPAppsPlatforms
namedArgs["host_label_updated_at"] = host.LabelUpdatedAt
if fleet.IsLinux(host.Platform) {
namedArgs["host_compatible_platforms"] = fleet.HostLinuxOSs
} else {
+22 -10
View File
@@ -1174,7 +1174,7 @@ WHERE
`
const deleteAllInstallerLabels = `
DELETE FROM
DELETE FROM
software_installer_labels
WHERE
software_installer_id = ?
@@ -1611,7 +1611,7 @@ func (ds *Datastore) IsSoftwareInstallerLabelScoped(ctx context.Context, install
SELECT 1 FROM (
-- no labels
SELECT 0 AS count_installer_labels, 0 AS count_host_labels
SELECT 0 AS count_installer_labels, 0 AS count_host_labels, 0 as count_host_updated_after_labels
WHERE NOT EXISTS (
SELECT 1 FROM software_installer_labels sil WHERE sil.software_installer_id = :installer_id
)
@@ -1621,7 +1621,8 @@ func (ds *Datastore) IsSoftwareInstallerLabelScoped(ctx context.Context, install
-- include any
SELECT
COUNT(*) AS count_installer_labels,
COUNT(lm.label_id) AS count_host_labels
COUNT(lm.label_id) AS count_host_labels,
0 as count_host_updated_after_labels
FROM
software_installer_labels sil
LEFT OUTER JOIN label_membership lm ON lm.label_id = sil.label_id
@@ -1630,23 +1631,34 @@ func (ds *Datastore) IsSoftwareInstallerLabelScoped(ctx context.Context, install
sil.software_installer_id = :installer_id
AND sil.exclude = 0
HAVING
count_installer_labels > 0 AND count_host_labels > 0
count_installer_labels > 0 AND count_host_labels > 0
UNION
-- exclude any
-- exclude any, ignore software that depends on labels created
-- _after_ the label_updated_at timestamp of the host (because
-- we don't have results for that label yet, the host may or may
-- not be a member).
SELECT
COUNT(*) AS count_installer_labels,
COUNT(lm.label_id) AS count_host_labels
COUNT(lm.label_id) AS count_host_labels,
SUM(CASE
WHEN
lbl.created_at IS NOT NULL AND (SELECT label_updated_at FROM hosts WHERE id = :host_id) >= lbl.created_at THEN 1
ELSE
0
END) as count_host_updated_after_labels
FROM
software_installer_labels sil
LEFT OUTER JOIN label_membership lm ON lm.label_id = sil.label_id
AND lm.host_id = :host_id
LEFT OUTER JOIN labels lbl
ON lbl.id = sil.label_id
LEFT OUTER JOIN label_membership lm
ON lm.label_id = sil.label_id AND lm.host_id = :host_id
WHERE
sil.software_installer_id = :installer_id
AND sil.exclude = 1
HAVING
count_installer_labels > 0 AND count_host_labels = 0
count_installer_labels > 0 AND count_installer_labels = count_host_updated_after_labels AND count_host_labels = 0
) t
`
namedArgs := map[string]any{
+41 -3
View File
@@ -5258,6 +5258,8 @@ func testListHostSoftwareWithLabelScoping(t *testing.T, ds *Datastore) {
nanoEnroll(t, ds, host, false)
user1 := test.NewUser(t, ds, "Alice", "alice@example.com", true)
time.Sleep(time.Second) // ensure the labels_updated_at timestamp is before labels creation
// create a software installer
tfr1, err := fleet.NewTempFileReader(strings.NewReader("hello"), t.TempDir)
require.NoError(t, err)
@@ -5324,6 +5326,10 @@ func testListHostSoftwareWithLabelScoping(t *testing.T, ds *Datastore) {
// assign the label to the host
require.NoError(t, ds.AddLabelsToHost(ctx, host.ID, []uint{label1.ID}))
host.LabelUpdatedAt = time.Now()
err = ds.UpdateHost(ctx, host)
require.NoError(t, err)
time.Sleep(time.Second)
// assign the label to the software installer
err = setOrUpdateSoftwareInstallerLabelsDB(ctx, ds.writer(ctx), installerID1, fleet.LabelIdentsWithScope{
@@ -5407,6 +5413,10 @@ func testListHostSoftwareWithLabelScoping(t *testing.T, ds *Datastore) {
// Now host has label1, label2
require.NoError(t, ds.AddLabelsToHost(ctx, host.ID, []uint{label2.ID}))
host.LabelUpdatedAt = time.Now()
err = ds.UpdateHost(ctx, host)
require.NoError(t, err)
time.Sleep(time.Second)
// List should be back to just installer1
software, _, err = ds.ListHostSoftware(ctx, host, opts)
@@ -5443,6 +5453,7 @@ func testListHostSoftwareWithLabelScoping(t *testing.T, ds *Datastore) {
installerID3, _, err := ds.MatchOrCreateSoftwareInstaller(ctx, installer3)
require.NoError(t, err)
time.Sleep(time.Second)
expectedInstallers[installer3.Filename] = &fleet.SoftwarePackageOrApp{
Name: installer3.Filename,
Version: installer3.Version,
@@ -5459,7 +5470,29 @@ func testListHostSoftwareWithLabelScoping(t *testing.T, ds *Datastore) {
})
require.NoError(t, err)
// We should have [installerID1, installerID3]
// We should have [installerID1, installerID3], but the exclude any label has
// no results for this host yet, so it's just installerID1 for now.
software, _, err = ds.ListHostSoftware(ctx, host, opts)
require.NoError(t, err)
require.Len(t, software, 1)
// installer1 is still in scope
scoped, err = ds.IsSoftwareInstallerLabelScoped(ctx, installerID1, host.ID)
require.NoError(t, err)
require.True(t, scoped)
// installer3 is not in scope yet, because label is "exclude any" and host doesn't have results
scoped, err = ds.IsSoftwareInstallerLabelScoped(ctx, installerID3, host.ID)
require.NoError(t, err)
require.False(t, scoped)
// mark as if label had been reported (but host is still not a member)
host.LabelUpdatedAt = time.Now()
err = ds.UpdateHost(ctx, host)
require.NoError(t, err)
time.Sleep(time.Second)
// now has 2 software (installer1 and 3)
software, _, err = ds.ListHostSoftware(ctx, host, opts)
require.NoError(t, err)
checkSoftware(software, installer2.Filename)
@@ -5470,9 +5503,9 @@ func testListHostSoftwareWithLabelScoping(t *testing.T, ds *Datastore) {
require.True(t, scoped)
// installer3 is in scope, because label is "exclude any" and host doesn't have the label
scoped, err = ds.IsSoftwareInstallerLabelScoped(ctx, installerID2, host.ID)
scoped, err = ds.IsSoftwareInstallerLabelScoped(ctx, installerID3, host.ID)
require.NoError(t, err)
require.False(t, scoped)
require.True(t, scoped)
// Now include hosts with label4. No host has this label, so we shouldn't see installerID3 anymore.
err = setOrUpdateSoftwareInstallerLabelsDB(ctx, ds.writer(ctx), installerID3, fleet.LabelIdentsWithScope{
@@ -5490,4 +5523,9 @@ func testListHostSoftwareWithLabelScoping(t *testing.T, ds *Datastore) {
scoped, err = ds.IsSoftwareInstallerLabelScoped(ctx, installerID1, host.ID)
require.NoError(t, err)
require.True(t, scoped)
// installer3 is not in scope
scoped, err = ds.IsSoftwareInstallerLabelScoped(ctx, installerID3, host.ID)
require.NoError(t, err)
require.False(t, scoped)
}