All files / src/resolvers RuleDefinitionResolvor.ts

100% Statements 50/50
90.91% Branches 20/22
100% Functions 13/13
100% Lines 45/45

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106    4x 4x       4x     3x 3x 3x         6x       12x 7x 6x 6x 1x 1x 1x 1x   5x 5x   5x   5x 5x           5x 5x       5x 5x       10x 10x   4x 4x             4x 2x   2x           5x 5x 5x   5x       5x 2x     5x 5x         10x 10x 8x 8x               2x 2x      
import { Logger, LoggerFactory } from "shared_types";
import { FlowObject, FlowRule, FlowRuleGroup, ResolvedFlowObject } from "shared_types";
import { ObjectDefinitionResolver } from "shared_types/src/resolvers/ObjectDefinitionResolver";
import { inject, injectable } from "tsyringe";
 
 
@injectable()
export class RuleDefinitionResolvor {
    logger: Logger;
 
    constructor(@inject('LoggerFactory') private loggerFactory: LoggerFactory,
        @inject('ObjectDefinitionResolver') private objectDefinitionResolver: ObjectDefinitionResolver) {
        this.logger = loggerFactory.getLogger('RuleDefinitionResolvor');
    }
 
    async resolveRules(ruleGroup: FlowRuleGroup, rules: FlowRule[], objects: FlowObject[], overallIndex: number): Promise<FlowRule[]> {
        // check if all object can be referenced
        return Promise.all(rules.map(rule => this.resolveRule(ruleGroup, rule, objects, overallIndex + rules.indexOf(rule))));
    }
    
    private async resolveRule(ruleGroup: FlowRuleGroup, rule: FlowRule, objects: FlowObject[], index: number): Promise<FlowRule> {
        const destinationObj = objects.find(obj => obj.id === rule.destination);
        const sourceObj = objects.find(obj => obj.id === rule.source);
        this.logger.info('resolving ', rule);
        if (!destinationObj || !sourceObj) {
            this.logger.error('invalid reference objects', destinationObj, sourceObj);
            rule.status = 'FAILED';
            rule.failureReasons = ['Unable to resolve reference object of source and/or target']
            return rule;
        }
        const resolvedSource = await this.resolveObject(ruleGroup, sourceObj)
        const resolvedDestination = await this.resolveObject(ruleGroup, destinationObj)
 
        this.updateRuleStatus(resolvedSource, rule, resolvedDestination);
 
        const sid = index + 1;
        const result = {
            ...rule,
 
            suricataString: this.createSuricataRuleString(rule, resolvedSource, resolvedDestination, sid)
 
        };
        this.logger.info('resolveRules -> resolveRule', result);
        return result;
    }
 
    private updateRuleStatus(resolvedSource: ResolvedFlowObject, rule: FlowRule, resolvedDestination: ResolvedFlowObject) {
        this.tryUpdateRuleWithAssociatedFailure(resolvedSource, rule, [`Can not resolve source object to address ${resolvedSource.id}`]);
        this.tryUpdateRuleWithAssociatedFailure(resolvedDestination, rule, [`Can not resolve destination object to address ${resolvedDestination.id}`]);
    }
 
    private tryUpdateRuleWithAssociatedFailure(resolvedSource: ResolvedFlowObject, rule: FlowRule, defaultReaons: [string]) {
        const hasReasons = (resolvedSource.failureReasons && resolvedSource.failureReasons.length > 0);
        if (resolvedSource.addresses.length === 0 || hasReasons) {
 
            rule.status = 'FAILED';
            rule.failureReasons = this.getFailedReasonPhrases(resolvedSource, hasReasons, defaultReaons);
 
        }
    }
 
    private getFailedReasonPhrases(resolvedSource: ResolvedFlowObject, hasReasons: boolean | undefined, canNotResolveToAddress: string[]) {
 
        if (hasReasons) {
            return resolvedSource.failureReasons;
        } else {
            return canNotResolveToAddress;
        }
 
    }
 
    private createSuricataRuleString(rule: FlowRule, resolvedSource: FlowObject, resolvedDestination: FlowObject, sid: number): string {
        const options = this.createRuleOptions(rule, sid);
        const sourcePortValue = rule.sourcePort.type === 'Any' ? 'any' : rule.sourcePort.value;
        const destinationPortValue = rule.destinationPort.type === 'Any' ? 'any' : rule.destinationPort.value;
 
        return `${rule.action} ${rule.protocol} ${resolvedSource.value} ${sourcePortValue} ->  ${resolvedDestination.value} ${destinationPortValue} ${options} `
    }
 
    private createRuleOptions(rule: FlowRule, sid: number) {
        const allAdditionalOptions = rule.optionFields?.map(kp => {
            return `${kp.key}: ${kp.value}`
        }
        ).join('; ');
        const appendedOptions = allAdditionalOptions? ' ' + allAdditionalOptions + ';' :'';
        return `(msg: "${rule.id}"; sid: ${sid};${appendedOptions})`;
    }
 
 
    private async resolveObject(ruleGroup: FlowRuleGroup, originalObj: FlowObject): Promise < ResolvedFlowObject > {
    try {
        const resolvedObject = await this.objectDefinitionResolver.resolveObject(originalObj, ruleGroup);
        this.logger.info('RuleDefinitionResolvor resolved object', resolvedObject);
        return {
            id: resolvedObject.id,
            type: resolvedObject.type,
            value: resolvedObject.addresses,
            addresses: resolvedObject.addresses,
            failureReasons: resolvedObject.failureReasons
        };
    } catch(e) {
        this.logger.info('got exception', e);
        return { ...originalObj, addresses: [], failureReasons: [e.message] }
    }
}
}