SecurityRiskAdvisors / VECTR

VECTR is a tool that facilitates tracking of your red and blue team testing activities to measure detection and prevention capabilities across different attack scenarios
1.37k stars 162 forks source link

Bug: Mutation createWithoutTemplate does not set outcomeNotes #269

Closed cwiph closed 3 months ago

cwiph commented 4 months ago

Describe the bug Since the upgrade to 9.0.2 the createWithoutTemplate graphql mutation does not set the outcomeNotes. It could be my buggy code but I would appreciate if you could look into it.

To Reproduce The following python script can be used to verify the issue. Please change the values accordingly in the below .env file and then execute the python script python3 whatever-you-name-it --config .env

API_KEY=xxxx:yyyyy
TARGET_ENVIRONMENT=YOUR_ENVIRONMENT
CAMPAIGN_ID=297
VECTR_GQL_URL=http://localhost:8080/sra-purpletools-rest/graphql
import argparse
from gql import Client, gql
from gql.transport.requests import RequestsHTTPTransport

class VectrGQLConnParams():
    api_key: str
    vectr_gql_url: str
    def __init__(self, api_key, vectr_gql_url):
        self.api_key = api_key
        self.vectr_gql_url = vectr_gql_url

def get_client(connection_params: VectrGQLConnParams):
    transport = RequestsHTTPTransport(
        url=connection_params.vectr_gql_url, verify=False, retries=1,
        headers={"Authorization": "VEC1 " + connection_params.api_key}
    )
    return Client(transport=transport, fetch_schema_from_transport=True)

def parse_dotenv(cfg):
    '''remove dotenv dependency and re-implement a simple parser'''
    with open(cfg, 'r') as f:
        data = f.readlines()
    config = {}
    for line in data:
        if line.startswith('#'):
            continue
        else:
            chunks = line.strip().split('=')
            config[chunks[0]] = '='.join(chunks[1:])
    return config

def debug_testcase_creation(connection_params, db, campaign_id):
    client = get_client(connection_params)

    test_case_mutation = gql(
        """
        mutation ($input: CreateTestCaseWithoutTemplateInput!) {
          testCase {
            createWithoutTemplate(input: $input) {
              testCases {
                id, name
              }
            }
          }
        }
        """
    )

    # basic testcase with some data
    input_data = [
        {
            "name": "Test Case Name",
            "description": "Description of the test case",
            "phase": "Privilege Escalation",
            "technique": "Technique used",
            "tags": ["tag1", "tag2"],
            "organization": "Debug",
            "status": "NotPerformed",  
            "targets": ["target1", "target2"],
            "sources": ["source1", "source2"],
            "detectionSteps": ["step1", "step2"],
            "preventionSteps": ["step1", "step2"],
            "outcomePath": "Logged.Logged_LocalTelemetry",
            "outcomeNotes": "Outcome notes",
            "references": ["reference1", "reference2"],
            "redTools": [{"name": "RedTool1"}, {"name": "RedTool2"}],
            "operatorGuidance": "Guidance for operator",
            "attackStart": 123.45,
            "attackStop": 234.56,
            "redTeamMetadata": [{"key": "key1", "value": "value1"}, {"key": "key2", "value": "value2"}],
            "blueTeamMetadata": [{"key": "key1", "value": "value1"}, {"key": "key2", "value": "value2"}],
        }
    ]

    test_case_vars = {
        "input": {
            "db": db,
            "campaignId": campaign_id,
            "testCaseData": input_data
        }
    }
    result = client.execute(test_case_mutation, variable_values=test_case_vars)

if __name__ == '__main__':

    parser = argparse.ArgumentParser(prog='import-vectr-testcase')
    parser.add_argument('--config', help='the .env / config file for VECTR', required=True)
    args = parser.parse_args()

    env_config = parse_dotenv(args.config)
    target_env = env_config["TARGET_ENVIRONMENT"]
    connection_params = VectrGQLConnParams(
        api_key=env_config["API_KEY"], vectr_gql_url=env_config["VECTR_GQL_URL"])

    debug_testcase_creation(connection_params=connection_params, db=target_env, campaign_id=env_config["CAMPAIGN_ID"])

Expected behavior The outcomeNotes hold the provided value Outcome notes

Screenshots outcome-notes

User Platform(please complete the following information):

VECTR Host(please complete the following information):

cwiph commented 4 months ago

if you add the outcomeNotes to the results of the GraphQL query, one can see that they are set to null

              testCases {
                id, name, outcomeNotes
              }

A workaround is to obtain the IDs of the testcases from the graphql result and then use the update(input: UpdateTestCaseInput!): UpdateTestCasePayload mutation to set the outcomeNotes

doodleincode commented 4 months ago

Thanks for reporting this. We've confirmed that this is a bug and will target a fix for our next release.

doodleincode commented 3 months ago

This has been fixed in 9.2.1.