Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: Mutation createWithoutTemplate does not set outcomeNotes #269

Closed
cwiph opened this issue May 23, 2024 · 3 comments
Closed

Bug: Mutation createWithoutTemplate does not set outcomeNotes #269

cwiph opened this issue May 23, 2024 · 3 comments

Comments

@cwiph
Copy link

cwiph commented May 23, 2024

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):

  • Browser latest Firefox

VECTR Host(please complete the following information):

  • Linux Distro: Linux vectr 5.15.0-107-generic 117-Ubuntu SMP Fri Apr 26 12:26:49 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
  • VECTR Version 9.0.2
@cwiph
Copy link
Author

cwiph commented May 24, 2024

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
Copy link
Contributor

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

@doodleincode
Copy link
Contributor

This has been fixed in 9.2.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants