diff --git a/frontend/src/views/geenforce/ManifestEditor.vue b/frontend/src/views/geenforce/ManifestEditor.vue index 19bd160..2917e25 100644 --- a/frontend/src/views/geenforce/ManifestEditor.vue +++ b/frontend/src/views/geenforce/ManifestEditor.vue @@ -334,7 +334,11 @@
- +
@@ -350,7 +354,11 @@
- +
@@ -408,6 +416,7 @@ + {{ detectionMethodHint }} Preinstall supports Registry/File detection only.
@@ -497,6 +506,7 @@ + What to do when a process below is running: Defer skips this cycle, CloseAndReopen restarts it, ForceClose kills it, ScheduleForReboot defers to next reboot.
@@ -569,7 +579,7 @@ import { describeEntry, availableEntryTypes as availableEntryTypesFor, availableDetectionMethods as availableDetectionMethodsFor, targetingGates as targetingGatesFor, targetingHint as targetingHintFor, - scopeSummary as scopeSummaryFor, + scopeSummary as scopeSummaryFor, detectionMethodHint as detectionMethodHintFor, } from './entryForm' const scopes = ref([]) @@ -588,6 +598,8 @@ const availableEntryTypes = computed(() => availableEntryTypesFor(isPreinstallScope.value, entryForm.value?.Type)) const availableDetectionMethods = computed(() => availableDetectionMethodsFor(isPreinstallScope.value, entryForm.value?.DetectionMethod)) +const detectionMethodHint = computed(() => + detectionMethodHintFor(entryForm.value?.DetectionMethod)) const scopeSummary = computed(() => scopeSummaryFor(detail.value)) const showAllTargeting = ref(false) const targetingGates = computed(() => targetingGatesFor(detail.value)) diff --git a/frontend/src/views/geenforce/entryForm.js b/frontend/src/views/geenforce/entryForm.js index c5cbc17..7384f63 100644 --- a/frontend/src/views/geenforce/entryForm.js +++ b/frontend/src/views/geenforce/entryForm.js @@ -1,11 +1,8 @@ // Pure, framework-free helpers for the GE-Enforce manifest editor. // -// These functions are lifted verbatim (behavior-for-behavior) from -// ManifestEditor.vue so they can be unit tested without mounting the whole -// component. ManifestEditor.vue still hosts its own copies today; a follow-up -// should point the component at this module (import from here) so the tested -// code and the shipped code are the same source. Until then keep the two in -// sync: any change to the editor logic must land here too. +// ManifestEditor.vue imports these directly (the component no longer keeps its +// own copies), so the unit tests in entryForm.spec.js exercise the shipped +// code path. Change the editor logic HERE. // // Everything here is a plain function of its inputs. No Vue, no reactivity, // no network. That is the whole point - deterministic logic we can pin down. @@ -16,6 +13,26 @@ export const DETECTION_METHODS = ['Registry', 'File', 'FileVersion', 'Hash', 'Ma 'ValueMatches', 'pnputil', 'Always'] export const INUSE_BEHAVIORS = ['Defer', 'CloseAndReopen', 'ForceClose', 'ScheduleForReboot'] +// Plain-language description of each detection method, shown under the Detection +// method dropdown so a first-time site admin understands what "present" means +// for the method they picked. Key '' is the no-detection case. +export const DETECTION_METHOD_HINTS = { + '': 'No detection rule: the action runs every cycle.', + Registry: 'Already correct if the registry value at Detection path/name exists (and equals Detection value when one is set).', + File: 'Already correct if the file at Detection path exists.', + FileVersion: 'Already correct if the file at Detection path is at Detection value or newer. This target feeds the Compliance panel.', + Hash: 'Already correct if the file at Detection path matches the SHA256 in Detection value. Re-copies when the file changed.', + MarkerFile: 'Already correct if the marker file at Detection path exists. Installs once, then the marker suppresses reruns.', + ValueMatches: 'Already correct if the registry value at Detection path/name equals Detection value exactly.', + pnputil: 'Already correct if a driver matching Detection pattern is staged in the Windows driver store. For INF entries.', + Always: 'Never counts as present, so the action runs every cycle. Same effect as no detection rule.', +} + +// Description for the currently selected detection method, or '' if unknown. +export function detectionMethodHint(method) { + return DETECTION_METHOD_HINTS[method || ''] || '' +} + // One blank entry form, matching the shape ManifestEditor seeds for a new entry. export function blankEntry() { return { Type: 'MSI', DetectionMethod: '', RegType: 'String', diff --git a/frontend/src/views/geenforce/entryForm.spec.js b/frontend/src/views/geenforce/entryForm.spec.js index f5591a4..bd29923 100644 --- a/frontend/src/views/geenforce/entryForm.spec.js +++ b/frontend/src/views/geenforce/entryForm.spec.js @@ -9,6 +9,7 @@ import { targetingHint, scopeSummary, describeEntry, + detectionMethodHint, ENTRY_TYPES, DETECTION_METHODS, } from './entryForm.js' @@ -300,3 +301,21 @@ describe('describeEntry', () => { expect(describeEntry({ Type: 'Weird', Name: 'x' })).toMatch(/^Applies x/) }) }) + +describe('detectionMethodHint', () => { + it('has a non-empty hint for every detection method', () => { + for (const method of DETECTION_METHODS) { + expect(detectionMethodHint(method).length).toBeGreaterThan(0) + } + }) + it('describes the no-detection case for empty/undefined', () => { + expect(detectionMethodHint('')).toMatch(/every cycle/) + expect(detectionMethodHint(undefined)).toMatch(/every cycle/) + }) + it('ties FileVersion to the compliance panel', () => { + expect(detectionMethodHint('FileVersion')).toMatch(/Compliance/) + }) + it('returns empty string for an unknown method', () => { + expect(detectionMethodHint('Nonsense')).toBe('') + }) +})