Skip to content

Commit

Permalink
[EDR Workflows] Add Signer option to Mac trusted apps (elastic#197821)
Browse files Browse the repository at this point in the history
This PR adds a Signer condition for trusted apps on macOS. Previously,
users could only build conditions using hash, path, and signer options
on Windows. With these changes, macOS also supports the Signer option,
leaving only Linux limited to Path and Hash options.


https://github.com/user-attachments/assets/ea8fb734-7884-451d-8873-e3a29861876b
  • Loading branch information
szwarckonrad authored Nov 18, 2024
1 parent ebd3f0d commit 55134ab
Show file tree
Hide file tree
Showing 14 changed files with 586 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,22 @@ export enum ConditionEntryField {
HASH = 'process.hash.*',
PATH = 'process.executable.caseless',
SIGNER = 'process.Ext.code_signature',
SIGNER_MAC = 'process.code_signature',
}

export enum EntryFieldType {
HASH = '.hash.',
EXECUTABLE = '.executable.caseless',
PATH = '.path',
SIGNER = '.Ext.code_signature',
SIGNER = '.code_signature',
}

export type TrustedAppConditionEntryField =
| 'process.hash.*'
| 'process.executable.caseless'
| 'process.Ext.code_signature';
| 'process.Ext.code_signature'
| 'process.code_signature';

export type BlocklistConditionEntryField =
| 'file.hash.*'
| 'file.path'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,9 @@ import type {
ExceptionListItemSchema,
CreateExceptionListItemSchema,
UpdateExceptionListItemSchema,
EntriesArray,
} from '@kbn/securitysolution-io-ts-list-types';
import {
ENDPOINT_EVENT_FILTERS_LIST_ID,
ENDPOINT_TRUSTED_APPS_LIST_ID,
ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID,
ENDPOINT_BLOCKLISTS_LIST_ID,
} from '@kbn/securitysolution-list-constants';
import { ENDPOINT_ARTIFACT_LISTS } from '@kbn/securitysolution-list-constants';
import { ConditionEntryField } from '@kbn/securitysolution-utils';
import { BaseDataGenerator } from './base_data_generator';
import { BY_POLICY_ARTIFACT_TAG_PREFIX, GLOBAL_ARTIFACT_TAG } from '../service/artifacts/constants';
Expand Down Expand Up @@ -150,7 +146,7 @@ export class ExceptionsListItemGenerator extends BaseDataGenerator<ExceptionList
generateTrustedApp(overrides: Partial<ExceptionListItemSchema> = {}): ExceptionListItemSchema {
return this.generate({
name: `Trusted app (${this.randomString(5)})`,
list_id: ENDPOINT_TRUSTED_APPS_LIST_ID,
list_id: ENDPOINT_ARTIFACT_LISTS.trustedApps.id,
...overrides,
});
}
Expand All @@ -173,10 +169,33 @@ export class ExceptionsListItemGenerator extends BaseDataGenerator<ExceptionList
};
}

generateTrustedAppSignerEntry(os = 'windows'): EntriesArray {
return [
{
field: os === 'windows' ? 'process.Ext.code_signature' : 'process.code_signature',
entries: [
{
field: 'trusted',
value: 'true',
type: 'match',
operator: 'included',
},
{
field: 'subject_name',
value: 'foo',
type: 'match',
operator: 'included',
},
],
type: 'nested',
},
];
}

generateEventFilter(overrides: Partial<ExceptionListItemSchema> = {}): ExceptionListItemSchema {
return this.generate({
name: `Event filter (${this.randomString(5)})`,
list_id: ENDPOINT_EVENT_FILTERS_LIST_ID,
list_id: ENDPOINT_ARTIFACT_LISTS.eventFilters.id,
entries: [
{
field: 'process.pe.company',
Expand Down Expand Up @@ -224,7 +243,7 @@ export class ExceptionsListItemGenerator extends BaseDataGenerator<ExceptionList
): ExceptionListItemSchema {
return this.generate({
name: `Host Isolation (${this.randomString(5)})`,
list_id: ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID,
list_id: ENDPOINT_ARTIFACT_LISTS.hostIsolationExceptions.id,
os_types: ['macos', 'linux', 'windows'],
entries: [
{
Expand Down Expand Up @@ -308,7 +327,7 @@ export class ExceptionsListItemGenerator extends BaseDataGenerator<ExceptionList

return this.generate({
name: `Blocklist ${this.randomString(5)}`,
list_id: ENDPOINT_BLOCKLISTS_LIST_ID,
list_id: ENDPOINT_ARTIFACT_LISTS.blocklists.id,
item_id: `generator_endpoint_blocklist_${this.seededUUIDv4()}`,
tags: [this.randomChoice([BY_POLICY_ARTIFACT_TAG_PREFIX, GLOBAL_ARTIFACT_TAG])],
os_types: [os],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import {
import { ConditionEntryField, OperatingSystem } from '@kbn/securitysolution-utils';
import type {
TrustedAppConditionEntry,
NewTrustedApp,
PutTrustedAppsRequestParams,
NewTrustedApp,
} from '../types';

describe('When invoking Trusted Apps Schema', () => {
Expand Down Expand Up @@ -105,14 +105,15 @@ describe('When invoking Trusted Apps Schema', () => {
value: 'c:/programs files/Anti-Virus',
...(data || {}),
});
const createNewTrustedApp = <T>(data?: T): NewTrustedApp => ({
name: 'Some Anti-Virus App',
description: 'this one is ok',
os: OperatingSystem.WINDOWS,
effectScope: { type: 'global' },
entries: [createConditionEntry()],
...(data || {}),
});
const createNewTrustedApp = <T>(data?: T): NewTrustedApp =>
({
name: 'Some Anti-Virus App',
description: 'this one is ok',
os: OperatingSystem.WINDOWS,
effectScope: { type: 'global' },
entries: [createConditionEntry()],
...(data || {}),
} as NewTrustedApp);
const body = PostTrustedAppCreateRequestSchema.body;

it('should not error on a valid message', () => {
Expand Down Expand Up @@ -389,14 +390,15 @@ describe('When invoking Trusted Apps Schema', () => {
value: 'c:/programs files/Anti-Virus',
...(data || {}),
});
const createNewTrustedApp = <T>(data?: T): NewTrustedApp => ({
name: 'Some Anti-Virus App',
description: 'this one is ok',
os: OperatingSystem.WINDOWS,
effectScope: { type: 'global' },
entries: [createConditionEntry()],
...(data || {}),
});
const createNewTrustedApp = <T>(data?: T): NewTrustedApp =>
({
name: 'Some Anti-Virus App',
description: 'this one is ok',
os: OperatingSystem.WINDOWS,
effectScope: { type: 'global' },
entries: [createConditionEntry()],
...(data || {}),
} as NewTrustedApp);

const updateParams = <T>(data?: T): PutTrustedAppsRequestParams => ({
id: 'validId',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ const LinuxEntrySchema = schema.object({

const MacEntrySchema = schema.object({
...CommonEntrySchema,
field: schema.oneOf([
schema.literal(ConditionEntryField.HASH),
schema.literal(ConditionEntryField.PATH),
schema.literal(ConditionEntryField.SIGNER_MAC),
]),
value: schema.string({
validate: (field: string) =>
field.length > 0 ? undefined : `invalidField.${ConditionEntryField.SIGNER_MAC}`,
}),
});

const entriesSchemaOptions = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,32 @@ export interface TrustedAppConditionEntry<T extends ConditionEntryField = Condit
value: string;
}

export type MacosLinuxConditionEntry = TrustedAppConditionEntry<
export type LinuxConditionEntry = TrustedAppConditionEntry<
ConditionEntryField.HASH | ConditionEntryField.PATH
>;
export type WindowsConditionEntry = TrustedAppConditionEntry<
ConditionEntryField.HASH | ConditionEntryField.PATH | ConditionEntryField.SIGNER
>;

export interface MacosLinuxConditionEntries {
os: OperatingSystem.LINUX | OperatingSystem.MAC;
entries: MacosLinuxConditionEntry[];
export type MacosConditionEntry = TrustedAppConditionEntry<
ConditionEntryField.HASH | ConditionEntryField.PATH | ConditionEntryField.SIGNER_MAC
>;

interface LinuxConditionEntries {
os: OperatingSystem.LINUX;
entries: LinuxConditionEntry[];
}

export interface WindowsConditionEntries {
interface WindowsConditionEntries {
os: OperatingSystem.WINDOWS;
entries: WindowsConditionEntry[];
}

interface MacosConditionEntries {
os: OperatingSystem.MAC;
entries: MacosConditionEntry[];
}

export interface GlobalEffectScope {
type: 'global';
}
Expand All @@ -70,7 +79,7 @@ export type NewTrustedApp = {
name: string;
description?: string;
effectScope: EffectScope;
} & (MacosLinuxConditionEntries | WindowsConditionEntries);
} & (LinuxConditionEntries | WindowsConditionEntries | MacosConditionEntries);

/** A trusted app entry */
export type TrustedApp = NewTrustedApp & {
Expand Down
Loading

0 comments on commit 55134ab

Please sign in to comment.