diff --git a/internal/commands/result.go b/internal/commands/result.go index 1afab952d..db3a245ca 100644 --- a/internal/commands/result.go +++ b/internal/commands/result.go @@ -559,18 +559,8 @@ func resultCodeBashing(codeBashingWrapper wrappers.CodeBashingWrapper) *cobra.Co ), RunE: runGetCodeBashingCommand(codeBashingWrapper), } - resultCmd.PersistentFlags().String(commonParams.LanguageFlag, "", "Language of the vulnerability") - err := resultCmd.MarkPersistentFlagRequired(commonParams.LanguageFlag) - if err != nil { - log.Fatal(err) - } - resultCmd.PersistentFlags().String(commonParams.VulnerabilityTypeFlag, "", "Vulnerability type") - err = resultCmd.MarkPersistentFlagRequired(commonParams.VulnerabilityTypeFlag) - if err != nil { - log.Fatal(err) - } - resultCmd.PersistentFlags().String(commonParams.CweIDFlag, "", "CWE ID for the vulnerability") - err = resultCmd.MarkPersistentFlagRequired(commonParams.CweIDFlag) + resultCmd.PersistentFlags().String(commonParams.QueryIDFlag, "", "QueryId of vulnerability") + err := resultCmd.MarkPersistentFlagRequired(commonParams.QueryIDFlag) if err != nil { log.Fatal(err) } @@ -1095,28 +1085,14 @@ func runGetCodeBashingCommand( codeBashingWrapper wrappers.CodeBashingWrapper, ) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { - language, _ := cmd.Flags().GetString(commonParams.LanguageFlag) - cwe, _ := cmd.Flags().GetString(commonParams.CweIDFlag) - vulType, _ := cmd.Flags().GetString(commonParams.VulnerabilityTypeFlag) - params, err := codeBashingWrapper.BuildCodeBashingParams( - []wrappers.CodeBashingParamsCollection{ - { - CweID: "CWE-" + cwe, - Language: language, - CxQueryName: strings.ReplaceAll(vulType, " ", "_"), - }, - }, - ) - if err != nil { - return err - } - // Fetch the cached token or a new one to obtain the codebashing URL incoded in the jwt token + + queryID, _ := cmd.Flags().GetString(commonParams.QueryIDFlag) codeBashingURL, err := codeBashingWrapper.GetCodeBashingURL(codeBashingKey) if err != nil { return err } // Make the request to the api to obtain the codebashing link and send the codebashing url to enrich the path - CodeBashingModel, webError, err := codeBashingWrapper.GetCodeBashingLinks(params, codeBashingURL) + CodeBashingModel, webError, err := codeBashingWrapper.GetCodeBashingLinks(queryID, codeBashingURL) if err != nil { return err } @@ -1124,6 +1100,10 @@ func runGetCodeBashingCommand( return errors.New(webError.Message) } err = printByFormat(cmd, *CodeBashingModel) + model := *CodeBashingModel + if len(model) > 0 && model[0].Path != "" { + logger.Printf("CodeBashing lesson available at: %s", model[0].Path) + } if err != nil { return errors.Wrapf(err, "%s", failedListingCodeBashing) } diff --git a/internal/commands/result_test.go b/internal/commands/result_test.go index e83c78a04..0a02f93ba 100644 --- a/internal/commands/result_test.go +++ b/internal/commands/result_test.go @@ -27,15 +27,14 @@ import ( const fileName = "cx_result" const ( - resultsCommand = "results" - codeBashingCommand = "codebashing" - vulnerabilityValue = "Reflected XSS All Clients" - languageValue = "PHP" - cweValue = "79" - jsonValue = "json" - tableValue = "table" - listValue = "list" - secretDetectionLine = "| Secret Detection 0 1 1 0 0 Completed |" + resultsCommand = "results" + codeBashingCommand = "codebashing" + queryIDValue = "8481125285487743346" + queryIDWrongValueValue = "11666704984804998184" + jsonValue = "json" + tableValue = "table" + listValue = "list" + secretDetectionLine = "| Secret Detection 0 1 1 0 0 Completed |" ) func flag(f string) string { @@ -553,40 +552,28 @@ func TestRunGetResultsByScanIdWithEmptyOutputPath(t *testing.T) { _ = execCmdNotNilAssertion(t, "results", "show", "--scan-id", "MOCK", "--output-path", "") } -func TestRunGetCodeBashingWithoutLanguage(t *testing.T) { +func TestRunGetCodeBashingWithEmptyQueryId(t *testing.T) { err := execCmdNotNilAssertion( t, resultsCommand, codeBashingCommand, - flag(params.CweIDFlag), - cweValue, - flag(params.VulnerabilityTypeFlag), - vulnerabilityValue) - assert.Equal(t, err.Error(), "required flag(s) \"language\" not set", "Wrong expected error message") + flag(params.QueryIDFlag), + "") + assert.Equal(t, err.Error(), "Cannot GET /lessons/mapping/", "Wrong expected error message") } -func TestRunGetCodeBashingWithoutVulnerabilityType(t *testing.T) { - err := execCmdNotNilAssertion( - t, +func TestRunGetCodeBashingWithEmptyQueryIdThatDoesNotHaveAnLesson(t *testing.T) { + cmd := createASTTestCommand() + buffer, err := executeRedirectedOsStdoutTestCommand(cmd, resultsCommand, codeBashingCommand, - flag(params.CweIDFlag), - cweValue, - flag(params.LanguageFlag), - languageValue) - assert.Equal(t, err.Error(), "required flag(s) \"vulnerability-type\" not set", "Wrong expected error message") -} + flag(params.QueryIDFlag), + queryIDWrongValueValue) -func TestRunGetCodeBashingWithoutCweId(t *testing.T) { - err := execCmdNotNilAssertion( - t, - resultsCommand, - codeBashingCommand, - flag(params.VulnerabilityTypeFlag), - vulnerabilityValue, - flag(params.LanguageFlag), - languageValue) - assert.Equal(t, err.Error(), "required flag(s) \"cwe-id\" not set", "Wrong expected error message") + assert.NilError(t, err, "Command should not return an error") + output := buffer.String() + assert.Assert(t, strings.Contains(output, "/app/home"), "Expected response to contain /app/home path") + assert.Assert(t, !strings.Contains(output, "Cannot GET /lessons/mapping/"), "Response should not contain error message") } func TestRunGetCodeBashingWithFormatJson(t *testing.T) { @@ -594,12 +581,8 @@ func TestRunGetCodeBashingWithFormatJson(t *testing.T) { t, resultsCommand, codeBashingCommand, - flag(params.VulnerabilityTypeFlag), - vulnerabilityValue, - flag(params.LanguageFlag), - languageValue, - flag(params.CweIDFlag), - cweValue, + flag(params.QueryIDFlag), + queryIDValue, flag(params.FormatFlag), jsonValue) } @@ -609,12 +592,8 @@ func TestRunGetCodeBashingWithFormatTable(t *testing.T) { t, resultsCommand, codeBashingCommand, - flag(params.VulnerabilityTypeFlag), - vulnerabilityValue, - flag(params.LanguageFlag), - languageValue, - flag(params.CweIDFlag), - cweValue, + flag(params.QueryIDFlag), + queryIDValue, flag(params.FormatFlag), tableValue) } @@ -624,12 +603,8 @@ func TestRunGetCodeBashingWithFormatList(t *testing.T) { t, resultsCommand, codeBashingCommand, - flag(params.VulnerabilityTypeFlag), - vulnerabilityValue, - flag(params.LanguageFlag), - languageValue, - flag(params.CweIDFlag), - cweValue, + flag(params.QueryIDFlag), + queryIDValue, flag(params.FormatFlag), listValue) } diff --git a/internal/params/binds.go b/internal/params/binds.go index 4dd138e19..c6e7c5142 100644 --- a/internal/params/binds.go +++ b/internal/params/binds.go @@ -16,7 +16,7 @@ var EnvVarsBinds = []struct { {IgnoreProxyKey, IgnoreProxyEnv, ""}, {AgentNameKey, AgentNameEnv, "ASTCLI"}, {OriginKey, OriginEnv, "CLI"}, - {CodeBashingPathKey, ScansPathEnv, "api/codebashing/lessons"}, + {CodeBashingPathKey, ScansPathEnv, "https://core-service.codebashing.com/lessons/mapping"}, {CustomStatesAPIPathKey, CustomStatesAPIPathEnv, "api/custom-states"}, {ScansPathKey, ScansPathEnv, "api/scans"}, {ProjectsPathKey, ProjectsPathEnv, "api/projects"}, diff --git a/internal/wrappers/client.go b/internal/wrappers/client.go index 032eb4e4b..e5b9d212d 100644 --- a/internal/wrappers/client.go +++ b/internal/wrappers/client.go @@ -44,10 +44,15 @@ const ( jwtError = "Error retrieving %s from jwt token" basicFormat = "Basic %s" bearerFormat = "Bearer %s" - contentTypeHeader = "Content-Type" - formURLContentType = "application/x-www-form-urlencoded" - jsonContentType = "application/json" - defaultDialerDuration = 30 * time.Second + // Can be removed once CodeBashing team will add support for + //http://core-service.codebashing.com/lessons/mapping/{queryId} + //with --header 'Authorization: Bearer ' currently + //supports only --header 'Authorization: ' + onlyTokenFormat = "%s" + contentTypeHeader = "Content-Type" + formURLContentType = "application/x-www-form-urlencoded" + jsonContentType = "application/json" + defaultDialerDuration = 30 * time.Second ) var ( @@ -339,6 +344,18 @@ func SendHTTPRequest(method, path string, body io.Reader, auth bool, timeout uin return SendHTTPRequestByFullURL(method, u, body, auth, timeout, accessToken, true) } +// Can be removed once CodeBashing team will add support for +// http://core-service.codebashing.com/lessons/mapping/{queryId} +// with --header 'Authorization: Bearer ' currently +// supports only --header 'Authorization: ' +func SendHTTPRequestNoBaseCBURL(method, path string, body io.Reader, auth bool, timeout uint) (*http.Response, error) { + _, accessToken, err := getURLAndAccessToken(path) + if err != nil { + return nil, err + } + return SendHTTPRequestWithoutBearerTagByFullURL(method, path, body, auth, timeout, accessToken, true) +} + func SendPrivateHTTPRequest(method, path string, body io.Reader, timeout uint, auth bool) (*http.Response, error) { u, accessToken, err := getURLAndAccessToken(path) if err != nil { @@ -358,6 +375,21 @@ func SendHTTPRequestByFullURL( return SendHTTPRequestByFullURLContentLength(method, fullURL, body, -1, auth, timeout, accessToken, bodyPrint) } +// Can be removed once CodeBashing team will add support for +// http://core-service.codebashing.com/lessons/mapping/{queryId} +// with --header 'Authorization: Bearer ' currently +// supports only --header 'Authorization: ' +func SendHTTPRequestWithoutBearerTagByFullURL( + method, fullURL string, + body io.Reader, + auth bool, + timeout uint, + accessToken string, + bodyPrint bool, +) (*http.Response, error) { + return SendHTTPRequestWithoutBearerTagByFullURLContentLength(method, fullURL, body, -1, auth, timeout, accessToken, bodyPrint) +} + func SendHTTPRequestByFullURLContentLength( method, fullURL string, body io.Reader, @@ -385,6 +417,37 @@ func SendHTTPRequestByFullURLContentLength( return request(client, req, bodyPrint) } +// Can be removed once CodeBashing team will add support for +// http://core-service.codebashing.com/lessons/mapping/{queryId} +// with --header 'Authorization: Bearer ' currently +// allow only --header 'Authorization: ' +func SendHTTPRequestWithoutBearerTagByFullURLContentLength( + method, fullURL string, + body io.Reader, + contentLength int64, + auth bool, + timeout uint, + accessToken string, + bodyPrint bool, +) (*http.Response, error) { + req, err := http.NewRequest(method, fullURL, body) + if err != nil { + return nil, err + } + if contentLength >= 0 { + req.ContentLength = contentLength + } + client := GetClient(timeout) + setAgentNameAndOrigin(req) + if auth { + enrichWithOath2Credentials(req, accessToken, onlyTokenFormat) + } + + req = addReqMonitor(req) + + return request(client, req, bodyPrint) +} + func addReqMonitor(req *http.Request) *http.Request { startTime := time.Now().UnixNano() / int64(time.Millisecond) if viper.GetBool(commonParams.DebugFlag) || viper.GetString(commonParams.LogFileFlag) != "" || viper.GetString(commonParams.LogFileConsoleFlag) != "" { diff --git a/internal/wrappers/codebashing-http.go b/internal/wrappers/codebashing-http.go index d7f295246..229a57b33 100644 --- a/internal/wrappers/codebashing-http.go +++ b/internal/wrappers/codebashing-http.go @@ -3,14 +3,18 @@ package wrappers import ( "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" + "strings" - commonParams "github.com/checkmarx/ast-cli/internal/params" - "github.com/checkmarx/ast-cli/internal/wrappers/utils" "github.com/golang-jwt/jwt/v5" "github.com/pkg/errors" "github.com/spf13/viper" + + "github.com/checkmarx/ast-cli/internal/logger" + commonParams "github.com/checkmarx/ast-cli/internal/params" + "github.com/checkmarx/ast-cli/internal/wrappers/configuration" + "github.com/checkmarx/ast-cli/internal/wrappers/utils" ) const ( @@ -20,6 +24,7 @@ const ( noCodebashingLinkAvailable = "No codebashing link available" licenseNotFoundExitCode = 3 lessonNotFoundExitCode = 4 + codeBashingDefaultPath = "https://core-service.codebashing.com/lessons/mapping" ) type CodeBashingHTTPWrapper struct { @@ -32,14 +37,14 @@ func NewCodeBashingHTTPWrapper(path string) *CodeBashingHTTPWrapper { } } -func (r *CodeBashingHTTPWrapper) GetCodeBashingLinks(params map[string]string, codeBashingURL string) ( +func (r *CodeBashingHTTPWrapper) GetCodeBashingLinks(queryID, codeBashingURL string) ( *[]CodeBashingCollection, *WebError, error, ) { + r.path = setCodeBashingDefaultPath(r.path) clientTimeout := viper.GetUint(commonParams.ClientTimeoutKey) - params[limit] = limitValue - resp, err := SendHTTPRequestWithQueryParams(http.MethodGet, r.path, params, nil, clientTimeout) + resp, err := SendHTTPRequestNoBaseCBURL(http.MethodGet, r.path+"/"+queryID, http.NoBody, true, clientTimeout) if err != nil { return nil, nil, err } @@ -59,20 +64,25 @@ func (r *CodeBashingHTTPWrapper) GetCodeBashingLinks(params map[string]string, c return nil, &errorModel, nil case http.StatusOK: var decoded []CodeBashingCollection - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return nil, nil, errors.Wrapf(err, failedToParseCodeBashing) } err = json.Unmarshal(body, &decoded) if err != nil { - return nil, nil, errors.Wrapf(err, failedToParseCodeBashing) + var wrappedResponse CodeBashingResponse + if err2 := json.Unmarshal(body, &wrappedResponse); err2 == nil { + decoded = []CodeBashingCollection{wrappedResponse.Data} + } else { + return nil, nil, errors.Wrapf(err, failedToParseCodeBashing) + } } /* Only check for position 0 because at the time we are only sending one queryName and getting as output one codebashing link. But it is possible to easily change it and be able to get multiple codebashing links */ - if decoded[0].Path == "" { + if len(decoded) == 0 || decoded[0].Path == "" { return nil, nil, NewAstError(lessonNotFoundExitCode, errors.Errorf(noCodebashingLinkAvailable)) } @@ -87,6 +97,21 @@ func (r *CodeBashingHTTPWrapper) GetCodeBashingLinks(params map[string]string, c } } +// Added this function to update the cx_code_bashing field in the existing config file. +func setCodeBashingDefaultPath(path string) string { + if path != codeBashingDefaultPath { + configFilePath, err := configuration.GetConfigFilePath() + if err != nil { + logger.PrintfIfVerbose("Error getting config file path: %v", err) + } + err = configuration.SafeWriteSingleConfigKeyString(configFilePath, strings.ToLower(commonParams.CodeBashingPathEnv), codeBashingDefaultPath) + if err != nil { + logger.PrintfIfVerbose("Error writing Risk Management path to config file: %v", err) + } + } + return codeBashingDefaultPath +} + func (r *CodeBashingHTTPWrapper) GetCodeBashingURL(field string) (string, error) { accessToken, err := GetAccessToken() if err != nil { @@ -112,7 +137,7 @@ func (r *CodeBashingHTTPWrapper) GetCodeBashingURL(field string) (string, error) return url, nil } -func (*CodeBashingHTTPWrapper) BuildCodeBashingParams(apiParams []CodeBashingParamsCollection) (map[string]string, error) { +func (*CodeBashingHTTPWrapper) BuildCodeBashingParams(apiParams CodeBashingParamsCollection) (map[string]string, error) { // Marshall entire object to string params := make(map[string]string) viewJSON, err := json.Marshal(apiParams) diff --git a/internal/wrappers/codebashing-json.go b/internal/wrappers/codebashing-json.go index c248627e3..8e3cefe3a 100644 --- a/internal/wrappers/codebashing-json.go +++ b/internal/wrappers/codebashing-json.go @@ -1,14 +1,27 @@ package wrappers +type CodeBashingTag struct { + UUID string `json:"uuid,omitempty"` + CWE string `json:"cwe,omitempty"` + OWASP string `json:"owasp,omitempty"` + SANS string `json:"sans,omitempty"` +} + type CodeBashingCollection struct { - Path string `json:"path,omitempty"` - CweID string `json:"cwe_id,omitempty"` - Language string `json:"lang,omitempty"` - CxQueryName string `json:"cxQueryName,omitempty"` + LessonDisplayName string `json:"lessonDisplayName,omitempty"` + CourseDisplayName string `json:"courseDisplayName,omitempty"` + Duration string `json:"duration,omitempty"` + ImageURL string `json:"imageUrl,omitempty"` + TagsResult []CodeBashingTag `json:"tagsResult,omitempty"` + Path string `json:"lessonUrl,omitempty"` + PotentialPoints int `json:"potentialPoints,omitempty"` +} + +// Wrapper struct to handle API responses with "data" field +type CodeBashingResponse struct { + Data CodeBashingCollection `json:"data,omitempty"` } type CodeBashingParamsCollection struct { - CweID string `json:"cwe_id,omitempty"` - Language string `json:"lang,omitempty"` - CxQueryName string `json:"cxQueryName,omitempty"` + QueryID string `json:"queryId,omitempty"` } diff --git a/internal/wrappers/codebashing.go b/internal/wrappers/codebashing.go index 4a5b3bf1b..fd332c5cb 100644 --- a/internal/wrappers/codebashing.go +++ b/internal/wrappers/codebashing.go @@ -1,7 +1,7 @@ package wrappers type CodeBashingWrapper interface { - GetCodeBashingLinks(params map[string]string, codeBashingURL string) (*[]CodeBashingCollection, *WebError, error) + GetCodeBashingLinks(queryID string, codeBashingURL string) (*[]CodeBashingCollection, *WebError, error) GetCodeBashingURL(field string) (string, error) - BuildCodeBashingParams([]CodeBashingParamsCollection) (map[string]string, error) + BuildCodeBashingParams(CodeBashingParamsCollection) (map[string]string, error) } diff --git a/internal/wrappers/mock/codebashing-mock.go b/internal/wrappers/mock/codebashing-mock.go index b4fc3ebcd..0bccdd304 100644 --- a/internal/wrappers/mock/codebashing-mock.go +++ b/internal/wrappers/mock/codebashing-mock.go @@ -6,23 +6,30 @@ import ( type CodeBashingMockWrapper struct{} -func (r CodeBashingMockWrapper) GetCodeBashingLinks(params map[string]string, codeBashingURL string) (*[]wrappers.CodeBashingCollection, *wrappers.WebError, error) { +func (r CodeBashingMockWrapper) GetCodeBashingLinks(queryID, _ string) (*[]wrappers.CodeBashingCollection, *wrappers.WebError, error) { + if queryID == "" { + return nil, &wrappers.WebError{Message: "Cannot GET /lessons/mapping/"}, nil + } + + if queryID == "11666704984804998184" { + collection := &wrappers.CodeBashingCollection{ + Path: "/app/home", + } + ret := []wrappers.CodeBashingCollection{*collection} + return &ret, nil, nil + } + collection := &wrappers.CodeBashingCollection{ - Path: "http://example.com/courses/php/lessons/dom_xss", - CweID: "CWE-79", - Language: "PHP", - CxQueryName: "Reflected_XSS_All_Clients", + Path: "http://example.com/courses/php/lessons/dom_xss", } ret := []wrappers.CodeBashingCollection{*collection} return &ret, nil, nil } -func (r CodeBashingMockWrapper) GetCodeBashingURL(field string) ( - string, error, -) { +func (r CodeBashingMockWrapper) GetCodeBashingURL(_ string) (string, error) { return "MOCK", nil } -func (r CodeBashingMockWrapper) BuildCodeBashingParams([]wrappers.CodeBashingParamsCollection) (map[string]string, error) { +func (r CodeBashingMockWrapper) BuildCodeBashingParams(_ wrappers.CodeBashingParamsCollection) (map[string]string, error) { return nil, nil } diff --git a/test/integration/data/config.yaml b/test/integration/data/config.yaml index 6d6b4c836..822096c37 100644 --- a/test/integration/data/config.yaml +++ b/test/integration/data/config.yaml @@ -17,7 +17,7 @@ cx_branch: "" cx_byor_path: api/byor cx_client_id: example_client_id cx_client_secret: example_client_secret -cx_codebashing_path: api/codebashing/lessons +cx_codebashing_path: https://core-service.codebashing.com/lessons/mapping cx_config_file_path: data/config.yaml cx_create_oath2_client_path: auth/realms/organization/pip/clients cx_custom_states_path: api/custom-states diff --git a/test/integration/result_test.go b/test/integration/result_test.go index ce09dca46..1a5ec44dd 100644 --- a/test/integration/result_test.go +++ b/test/integration/result_test.go @@ -21,10 +21,11 @@ import ( ) const ( - fileName = "result-test" - resultsDirectory = "output-results-folder/" - fileExtention = "report.json" - + fileName = "result-test" + resultsDirectory = "output-results-folder/" + fileExtention = "report.json" + queryIDWrongValueValue = "11666704984804998184" + queryIDValue = "8481125285487743346" //---------------------------------------------------------------------------------------------------------------------- // This ScanIDWithDevAndTestDep is associated with the CXOne project: ASTCLI/HideDevAndTestsVulnerabilities/Test (DEU, Galactica tenant). // All vulnerable packages in this project have been snoozed or muted, so no vulnerabilities should appear in this scan. @@ -192,7 +193,7 @@ func TestCodeBashingParamFailed(t *testing.T) { } err, _ := executeCommand(t, args...) - assertError(t, err, "required flag(s) \"cwe-id\", \"language\", \"vulnerability-type\" not set") + assertError(t, err, "required flag(s) \"query-id\" not set") } func TestCodeBashingList(t *testing.T) { @@ -201,9 +202,8 @@ func TestCodeBashingList(t *testing.T) { "Getting results should pass", "results", "codebashing", - flag(params.LanguageFlag), "PHP", - flag(params.VulnerabilityTypeFlag), "Reflected XSS All Clients", - flag(params.CweIDFlag), "79") + "--debug", + flag(params.QueryIDFlag), queryIDValue) codebashing := []wrappers.CodeBashingCollection{} @@ -218,9 +218,8 @@ func TestCodeBashingListJson(t *testing.T) { "Getting results should pass", "results", "codebashing", - flag(params.LanguageFlag), "PHP", - flag(params.VulnerabilityTypeFlag), "Reflected XSS All Clients", - flag(params.CweIDFlag), "79", + "--debug", + flag(params.QueryIDFlag), queryIDValue, flag(params.FormatFlag), "json") codebashing := []wrappers.CodeBashingCollection{} @@ -236,34 +235,35 @@ func TestCodeBashingListTable(t *testing.T) { "Getting results should pass", "results", "codebashing", - flag(params.LanguageFlag), "PHP", - flag(params.VulnerabilityTypeFlag), "Reflected XSS All Clients", - flag(params.CweIDFlag), "79", + "--debug", + flag(params.QueryIDFlag), queryIDValue, flag(params.FormatFlag), "table") assert.Assert(t, outputBuffer != nil, "Should exist codebashing link") } func TestCodeBashingListEmpty(t *testing.T) { - args := []string{ + outputBuffer := executeCmdNilAssertion( + t, + "Getting results should pass", "results", "codebashing", - flag(params.LanguageFlag), "PHP", - flag(params.VulnerabilityTypeFlag), "Reflected XSS All Clients", - flag(params.CweIDFlag), "11", - } + "--debug", + flag(params.QueryIDFlag), queryIDWrongValueValue) - err, _ := executeCommand(t, args...) - assertError(t, err, "No codebashing link available") + assert.Assert(t, outputBuffer != nil, "Output buffer should not be nil") + output := outputBuffer.String() + assert.Assert(t, + strings.Contains(output, "lessonUrl") && + strings.Contains(output, "/app/home"), + "Output should contain expected codebashing lesson information") } func TestCodeBashingFailedListingAuth(t *testing.T) { args := []string{ "results", "codebashing", - flag(params.LanguageFlag), "PHP", - flag(params.VulnerabilityTypeFlag), "Reflected XSS All Clients", - flag(params.CweIDFlag), "11", + flag(params.QueryIDFlag), queryIDValue, flag(params.AccessKeySecretFlag), "mock", flag(params.AccessKeyIDFlag), "mock", flag(params.AstAPIKeyFlag), "mock",