diff --git a/Gemfile b/Gemfile index a9ba5cef2a3ca3b268d385862c8750dfb181af5f..f0ba023c8e0b6037dd1b26a0735895336858e174 100644 --- a/Gemfile +++ b/Gemfile @@ -55,7 +55,7 @@ end gem 'gitlab-backup-cli', path: 'gems/gitlab-backup-cli', require: 'gitlab/backup/cli', feature_category: :backup_restore -gem 'gitlab-secret_detection', '< 1.0', feature_category: :secret_detection +gem 'gitlab-secret_detection', path: 'gems/gitlab-secret_detection', feature_category: :secret_detection # Responders respond_to and respond_with gem 'responders', '~> 3.0' # rubocop:todo Gemfile/MissingFeatureCategory diff --git a/Gemfile.checksum b/Gemfile.checksum index 9a67f063be5b2f069408958e5f34af9d617be7a1..77b6d8684cf680cdf24b263f170ce153174207db 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -238,7 +238,6 @@ {"name":"gitlab-markup","version":"1.9.0","platform":"ruby","checksum":"7eda045a08ec2d110084252fa13a8c9eac8bdac0e302035ca7db4b82bcbd7ed4"}, {"name":"gitlab-net-dns","version":"0.9.2","platform":"ruby","checksum":"f726d978479d43810819f12a45c0906d775a07e34df111bbe693fffbbef3059d"}, {"name":"gitlab-sdk","version":"0.3.1","platform":"ruby","checksum":"48ba49084f4ab92df7c7ef9f347020d9dfdf6ed9c1e782b67264e98ffe6ea710"}, -{"name":"gitlab-secret_detection","version":"0.14.2","platform":"ruby","checksum":"c6d3bc92b47cdf930ff7bf1e519a849353f33df1a2b4493078963769854850f0"}, {"name":"gitlab-security_report_schemas","version":"0.1.2.min15.0.0.max15.2.1","platform":"ruby","checksum":"300037487ec9d51a814f648514ff521cb82b94fc51d9fe53389175b36ac680ae"}, {"name":"gitlab-styles","version":"13.0.2","platform":"ruby","checksum":"e662b9334643763b55a861f9e26091096547f98179bd89b0fa8d6c6fb8cec861"}, {"name":"gitlab_chronic_duration","version":"0.12.0","platform":"ruby","checksum":"0d766944d415b5c831f176871ee8625783fc0c5bfbef2d79a3a616f207ffc16d"}, diff --git a/Gemfile.lock b/Gemfile.lock index cd631a302ecdbd5996c10463066e17f15f748c5f..12b7bf5104ecb3cdcc5b43aa8b867deb3ecf3475 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -105,6 +105,16 @@ PATH diffy pg_query +PATH + remote: gems/gitlab-secret_detection + specs: + gitlab-secret_detection (0.1.1) + grpc (= 1.63.0) + grpc-tools (= 1.63.0) + parallel (~> 1.22) + re2 (~> 2.4) + toml-rb (~> 2.2) + PATH remote: gems/gitlab-utils specs: @@ -772,12 +782,6 @@ GEM activesupport (>= 5.2.0) rake (~> 13.0) snowplow-tracker (~> 0.8.0) - gitlab-secret_detection (0.14.2) - grpc (= 1.63.0) - grpc-tools (= 1.63.0) - parallel (~> 1.19) - re2 (= 2.7.0) - toml-rb (~> 2.2.0) gitlab-security_report_schemas (0.1.2.min15.0.0.max15.2.1) activesupport (>= 6, < 8) json_schemer (~> 2.3.0) @@ -2097,7 +2101,7 @@ DEPENDENCIES gitlab-safe_request_store! gitlab-schema-validation! gitlab-sdk (~> 0.3.0) - gitlab-secret_detection (< 1.0) + gitlab-secret_detection! gitlab-security_report_schemas (= 0.1.2.min15.0.0.max15.2.1) gitlab-sidekiq-fetcher! gitlab-styles (~> 13.0.2) diff --git a/Gemfile.next.checksum b/Gemfile.next.checksum index 0be7039c3bcbab87399313199f919fc6207803d3..6c4860891368bb17c3758ada77fa98922be40a68 100644 --- a/Gemfile.next.checksum +++ b/Gemfile.next.checksum @@ -239,7 +239,6 @@ {"name":"gitlab-markup","version":"1.9.0","platform":"ruby","checksum":"7eda045a08ec2d110084252fa13a8c9eac8bdac0e302035ca7db4b82bcbd7ed4"}, {"name":"gitlab-net-dns","version":"0.9.2","platform":"ruby","checksum":"f726d978479d43810819f12a45c0906d775a07e34df111bbe693fffbbef3059d"}, {"name":"gitlab-sdk","version":"0.3.1","platform":"ruby","checksum":"48ba49084f4ab92df7c7ef9f347020d9dfdf6ed9c1e782b67264e98ffe6ea710"}, -{"name":"gitlab-secret_detection","version":"0.14.2","platform":"ruby","checksum":"c6d3bc92b47cdf930ff7bf1e519a849353f33df1a2b4493078963769854850f0"}, {"name":"gitlab-security_report_schemas","version":"0.1.2.min15.0.0.max15.2.1","platform":"ruby","checksum":"300037487ec9d51a814f648514ff521cb82b94fc51d9fe53389175b36ac680ae"}, {"name":"gitlab-styles","version":"13.0.2","platform":"ruby","checksum":"e662b9334643763b55a861f9e26091096547f98179bd89b0fa8d6c6fb8cec861"}, {"name":"gitlab_chronic_duration","version":"0.12.0","platform":"ruby","checksum":"0d766944d415b5c831f176871ee8625783fc0c5bfbef2d79a3a616f207ffc16d"}, diff --git a/Gemfile.next.lock b/Gemfile.next.lock index f3049898b9f05242c5e062d82e67f4eadc54c7ce..bd53ceff22035db527b8a159302c850b4c983388 100644 --- a/Gemfile.next.lock +++ b/Gemfile.next.lock @@ -105,6 +105,16 @@ PATH diffy pg_query +PATH + remote: gems/gitlab-secret_detection + specs: + gitlab-secret_detection (0.1.1) + grpc (= 1.63.0) + grpc-tools (= 1.63.0) + parallel (~> 1.22) + re2 (~> 2.4) + toml-rb (~> 2.2) + PATH remote: gems/gitlab-utils specs: @@ -785,12 +795,6 @@ GEM activesupport (>= 5.2.0) rake (~> 13.0) snowplow-tracker (~> 0.8.0) - gitlab-secret_detection (0.14.2) - grpc (= 1.63.0) - grpc-tools (= 1.63.0) - parallel (~> 1.19) - re2 (= 2.7.0) - toml-rb (~> 2.2.0) gitlab-security_report_schemas (0.1.2.min15.0.0.max15.2.1) activesupport (>= 6, < 8) json_schemer (~> 2.3.0) @@ -2130,7 +2134,7 @@ DEPENDENCIES gitlab-safe_request_store! gitlab-schema-validation! gitlab-sdk (~> 0.3.0) - gitlab-secret_detection (< 1.0) + gitlab-secret_detection! gitlab-security_report_schemas (= 0.1.2.min15.0.0.max15.2.1) gitlab-sidekiq-fetcher! gitlab-styles (~> 13.0.2) diff --git a/app/assets/javascripts/gl_field_error.js b/app/assets/javascripts/gl_field_error.js index 08361b9ffaceef2fb00408c6d5b00c732c643735..98e4b7f988c9ebc20222724ace04bb60dab85a06 100644 --- a/app/assets/javascripts/gl_field_error.js +++ b/app/assets/javascripts/gl_field_error.js @@ -124,8 +124,8 @@ export default class GlFieldError { // For UX, wait til after first invalid submission to check each keyup // eslint-disable-next-line @gitlab/no-global-event-off this.inputElement - .off('keyup.fieldValidator') - .on('keyup.fieldValidator', this.updateValidity.bind(this)); + .off('input.fieldValidator') + .on('input.fieldValidator', this.updateValidity.bind(this)); } /* Get or set current input value */ diff --git a/app/assets/javascripts/gl_field_errors.js b/app/assets/javascripts/gl_field_errors.js index 28aa9906116caa14c4f9d3a02b420047435da8ce..f8e6999c562d4eed46813c4091e6f65451aaccb3 100644 --- a/app/assets/javascripts/gl_field_errors.js +++ b/app/assets/javascripts/gl_field_errors.js @@ -22,6 +22,7 @@ export default class GlFieldErrors { 'input[type=url]', 'input[type=number]', 'textarea', + 'select', ].join(','); this.state.inputs = this.form diff --git a/ee/app/assets/javascripts/registrations/company/new/index.js b/ee/app/assets/javascripts/registrations/company/new/index.js index 4213e2519124b1f90dfa463acd95b9ee23906ee5..77ff1b584c9f8258a2da8567614ea564d4410c3e 100644 --- a/ee/app/assets/javascripts/registrations/company/new/index.js +++ b/ee/app/assets/javascripts/registrations/company/new/index.js @@ -1,8 +1,9 @@ import Vue from 'vue'; import apolloProvider from 'ee/subscriptions/graphql/graphql'; import CompanyForm from 'ee/registrations/components/company_form.vue'; +import GlFieldErrors from '~/gl_field_errors'; -export default () => { +const mountCompanyForm = () => { const el = document.querySelector('#js-registrations-company-form'); const { submitPath, firstName, lastName, emailDomain, formType, trackActionForErrors } = el.dataset; @@ -25,3 +26,10 @@ export default () => { }, }); }; + +export default () => { + mountCompanyForm(); + + // Since we replaced form inputs, we need to re-initialize the field errors handler + return new GlFieldErrors(document.querySelectorAll('.gl-show-field-errors')); +}; diff --git a/ee/app/assets/javascripts/registrations/components/company_form.vue b/ee/app/assets/javascripts/registrations/components/company_form.vue index 5ada574a174d5b5d74f30804e931647f14c032f6..ec1499edd4100b0df65cab4583eb0dede3c635cc 100644 --- a/ee/app/assets/javascripts/registrations/components/company_form.vue +++ b/ee/app/assets/javascripts/registrations/components/company_form.vue @@ -106,7 +106,12 @@ export default { </script> <template> - <gl-form :action="submitPath" method="post" @submit="trackCompanyForm"> + <gl-form + :action="submitPath" + class="gl-show-field-errors" + method="post" + @submit="trackCompanyForm" + > <input :value="$options.csrf.token" type="hidden" name="authenticity_token" /> <p data-testid="description" class="gl-mt-2">{{ $options.i18n.description[formType] }}</p> <div class="gl-mt-6 gl-flex gl-flex-col sm:gl-flex-row"> @@ -157,6 +162,7 @@ export default { <gl-form-group :label="$options.i18n.companySizeLabel" label-size="sm" label-for="company_size"> <gl-form-select id="company_size" + class="gl-field-error-anchor" :value="companySize" name="company_size" select-class="js-track-error" diff --git a/ee/app/assets/javascripts/trials/components/country_or_region_selector.vue b/ee/app/assets/javascripts/trials/components/country_or_region_selector.vue index 58725d992d38b317a728c1909cef53f6cc8eb610..d032fbf1203c114ca06b4a8de2765a5e44812f81 100644 --- a/ee/app/assets/javascripts/trials/components/country_or_region_selector.vue +++ b/ee/app/assets/javascripts/trials/components/country_or_region_selector.vue @@ -49,7 +49,7 @@ export default { stateSelectPrompt: TRIAL_STATE_PROMPT, }, computed: { - countryClass() { + trackClass() { return this.trackActionForErrors ? 'js-track-error' : ''; }, countryOptionsWithDefault() { @@ -126,7 +126,8 @@ export default { id="country" v-model="selectedCountry" name="country" - :select-class="countryClass" + class="gl-field-error-anchor" + :select-class="trackClass" :options="countryOptionsWithDefault" value-field="id" text-field="name" @@ -137,21 +138,24 @@ export default { /> </gl-form-group> <gl-form-group - v-if="showState" + :class="{ 'gl-hidden': !showState }" :label="$options.i18n.stateLabel" label-size="sm" label-for="state" + data-testid="state-form-group" > <gl-form-select id="state" v-model="selectedState" v-autofocusonshow name="state" + class="gl-field-error-anchor" + :select-class="trackClass" :options="stateOptionsWithDefault" value-field="id" text-field="name" data-testid="state-dropdown" - :required="required" + :required="showState" @change="selected" /> </gl-form-group> diff --git a/ee/lib/gitlab/checks/secrets_check.rb b/ee/lib/gitlab/checks/secrets_check.rb index 268be5ccf2809d3da5bfc6c1ff946bca32544903..2a91df7a669c58bf309d6dfe291c73eda46a9679 100644 --- a/ee/lib/gitlab/checks/secrets_check.rb +++ b/ee/lib/gitlab/checks/secrets_check.rb @@ -6,8 +6,8 @@ class SecretsCheck < ::Gitlab::Checks::BaseBulkChecker include Gitlab::InternalEventsTracking ERROR_MESSAGES = { - failed_to_scan_regex_error: "\n - Failed to scan blob(id: %{payload_id}) due to regex error.", - blob_timed_out_error: "\n - Scanning blob(id: %{payload_id}) timed out.", + failed_to_scan_regex_error: "\n - Failed to scan blob(id: %{blob_id}) due to regex error.", + blob_timed_out_error: "\n - Scanning blob(id: %{blob_id}) timed out.", scan_timeout_error: 'Secret detection scan timed out.', scan_initialization_error: 'Secret detection scan failed to initialize.', invalid_input_error: 'Secret detection scan failed due to invalid input.', @@ -29,7 +29,7 @@ class SecretsCheck < ::Gitlab::Checks::BaseBulkChecker "found the following secrets in commit: %{sha}", finding_message_occurrence_path: "\n-- %{path}:", finding_message_occurrence_line: "%{line_number} | %{description}", - finding_message: "\n\nSecret leaked in blob: %{payload_id}" \ + finding_message: "\n\nSecret leaked in blob: %{blob_id}" \ "\n -- line:%{line_number} | %{description}", found_secrets_footer: "\n--------------------------------------------------\n\n" }.freeze @@ -39,11 +39,10 @@ class SecretsCheck < ::Gitlab::Checks::BaseBulkChecker DOCUMENTATION_PATH = 'user/application_security/secret_detection/secret_push_protection/index.html' DOCUMENTATION_PATH_ANCHOR = 'resolve-a-blocked-push' EXCLUSION_TYPE_MAP = { - rule: ::Gitlab::SecretDetection::GRPC::ExclusionType::EXCLUSION_TYPE_RULE, - path: ::Gitlab::SecretDetection::GRPC::ExclusionType::EXCLUSION_TYPE_PATH, - raw_value: ::Gitlab::SecretDetection::GRPC::ExclusionType::EXCLUSION_TYPE_RAW_VALUE + path: ::Gitlab::SecretDetection::GRPC::ScanRequest::ExclusionType::EXCLUSION_TYPE_RULE, + raw_value: ::Gitlab::SecretDetection::GRPC::ScanRequest::ExclusionType::EXCLUSION_TYPE_RAW_VALUE }.freeze - UNKNOWN_EXCLUSION_TYPE = ::Gitlab::SecretDetection::GRPC::ExclusionType::EXCLUSION_TYPE_UNSPECIFIED + UNKNOWN_EXCLUSION_TYPE = ::Gitlab::SecretDetection::GRPC::ScanRequest::ExclusionType::EXCLUSION_TYPE_UNSPECIFIED # HUNK_HEADER_REGEX matches a line starting with @@, followed by - and digits (starting line number # and range in the original file, comma and more digits optional), then + and digits (starting line number @@ -108,18 +107,10 @@ def validate! send_request_to_sds(payloads, exclusions: active_exclusions) - rules = ::Gitlab::SecretDetection::Core::Ruleset.new( - logger: secret_detection_logger - ).rules - # Pass payloads to gem for scanning. - response = ::Gitlab::SecretDetection::Core::Scanner - .new(rules: rules, logger: secret_detection_logger) - .secrets_scan( - payloads, - timeout: logger.time_left, - exclusions: active_exclusions - ) + response = ::Gitlab::SecretDetection::Scan + .new(logger: secret_detection_logger) + .secrets_scan(payloads, timeout: logger.time_left, exclusions: active_exclusions) # Log audit events for exlusions that were applied. log_applied_exclusions_audit_events(response.applied_exclusions) @@ -128,9 +119,8 @@ def validate! format_response(response) # TODO: Perhaps have a separate message for each and better logging? - rescue ::Gitlab::SecretDetection::Core::Scanner::RulesetParseError, - ::Gitlab::SecretDetection::Core::Scanner::RulesetCompilationError => e - + rescue ::Gitlab::SecretDetection::Scan::RulesetParseError, + ::Gitlab::SecretDetection::Scan::RulesetCompilationError => _ secret_detection_logger.error(message: ERROR_MESSAGES[:scan_initialization_error]) end end @@ -198,18 +188,10 @@ def log_applied_exclusions_audit_events(applied_exclusions) # scanning of either `rule` or `raw_value` type. For `path` exclusions, we create the audit events # when applied while formatting the response. applied_exclusions.each do |exclusion| - project_security_exclusion = get_project_security_exclusion_from_sds_exclusion(exclusion) - log_exclusion_audit_event(project_security_exclusion) unless project_security_exclusion.nil? + log_exclusion_audit_event(exclusion) end end - def get_project_security_exclusion_from_sds_exclusion(exclusion) - return exclusion if exclusion.is_a?(::Security::ProjectSecurityExclusion) - - # TODO When we implement 2-way SDS communication, we should add the type to this lookup - project.security_exclusions.where(value: exclusion.value).first # rubocop:disable CodeReuse/ActiveRecord -- Need to be able to link GRPC::Exclusion to ProjectSecurityExclusion - end - def log_exclusion_audit_event(exclusion) audit_context = { name: 'project_security_exclusion_applied', @@ -261,8 +243,7 @@ def standardize_payloads payloads = get_diffs payloads = payloads.flat_map do |payload| - p_ary = parse_diffs(payload) - build_payloads(p_ary) + parse_diffs(payload) end payloads.compact.empty? ? nil : payloads @@ -275,13 +256,11 @@ def standardize_payloads payloads.reject! { |payload| payload.size > PAYLOAD_BYTES_LIMIT || payload.binary } payloads.map do |payload| - build_payload( - { - id: payload.id, - data: payload.data, - offset: 1 - } - ) + { + id: payload.id, + data: payload.data, + offset: 1 + } end end end @@ -436,8 +415,8 @@ def revisions def format_response(response) # Try to retrieve file path and commit sha for the diffs found. if [ - ::Gitlab::SecretDetection::Core::Status::FOUND, - ::Gitlab::SecretDetection::Core::Status::FOUND_WITH_ERRORS + ::Gitlab::SecretDetection::Status::FOUND, + ::Gitlab::SecretDetection::Status::FOUND_WITH_ERRORS ].include?(response.status) results = transform_findings(response) @@ -447,17 +426,17 @@ def format_response(response) end case response.status - when ::Gitlab::SecretDetection::Core::Status::NOT_FOUND + when ::Gitlab::SecretDetection::Status::NOT_FOUND # No secrets found, we log and skip the check. secret_detection_logger.info(message: LOG_MESSAGES[:secrets_not_found]) - when ::Gitlab::SecretDetection::Core::Status::FOUND + when ::Gitlab::SecretDetection::Status::FOUND # One or more secrets found, generate message with findings and fail check. message = build_secrets_found_message(results) secret_detection_logger.info(message: LOG_MESSAGES[:found_secrets]) raise ::Gitlab::GitAccess::ForbiddenError, message - when ::Gitlab::SecretDetection::Core::Status::FOUND_WITH_ERRORS + when ::Gitlab::SecretDetection::Status::FOUND_WITH_ERRORS # One or more secrets found, but with scan errors, so we # generate a message with findings and errors, and fail the check. message = build_secrets_found_message(results, with_errors: true) @@ -465,12 +444,12 @@ def format_response(response) secret_detection_logger.info(message: LOG_MESSAGES[:found_secrets_with_errors]) raise ::Gitlab::GitAccess::ForbiddenError, message - when ::Gitlab::SecretDetection::Core::Status::SCAN_TIMEOUT + when ::Gitlab::SecretDetection::Status::SCAN_TIMEOUT # Entire scan timed out, we log and skip the check for now. secret_detection_logger.error(message: ERROR_MESSAGES[:scan_timeout_error]) - when ::Gitlab::SecretDetection::Core::Status::INPUT_ERROR - # Scan failed due to invalid input. We skip the check because of an input error - # which could be due to not having anything to scan. + when ::Gitlab::SecretDetection::Status::INPUT_ERROR + # Scan failed due to invalid input. We skip the check because an input error + # could be due to not having `diffs` being empty (i.e. no new diffs to scan). secret_detection_logger.error(message: ERROR_MESSAGES[:invalid_input_error]) else # Invalid status returned by the scanning service/gem, we don't @@ -517,7 +496,7 @@ def build_secrets_found_message(results, with_errors: false) def build_finding_message(finding, type) case finding.status - when ::Gitlab::SecretDetection::Core::Status::FOUND + when ::Gitlab::SecretDetection::Status::FOUND track_secret_found(finding.description) case type @@ -526,9 +505,9 @@ def build_finding_message(finding, type) when :blob build_blob_finding_message(finding) end - when ::Gitlab::SecretDetection::Core::Status::SCAN_ERROR + when ::Gitlab::SecretDetection::Status::SCAN_ERROR format(ERROR_MESSAGES[:failed_to_scan_regex_error], finding.to_h) - when ::Gitlab::SecretDetection::Core::Status::PAYLOAD_TIMEOUT + when ::Gitlab::SecretDetection::Status::PAYLOAD_TIMEOUT format(ERROR_MESSAGES[:blob_timed_out_error], finding.to_h) end end @@ -550,7 +529,7 @@ def build_blob_finding_message(finding) # rubocop:disable Metrics/CyclomaticComplexity -- Not easy to move complexity away into other methods, entire method will be refactored shortly. def transform_findings(response) # Let's group the findings by the blob id. - findings_by_blobs = response.results.group_by(&:payload_id) + findings_by_blobs = response.results.group_by(&:blob_id) # We create an empty hash for the structure we'll create later as we pull out tree entries. findings_by_commits = {} @@ -593,7 +572,7 @@ def transform_findings(response) # is available, we will likely be able move this check to the gem/secret detection service # since paths will be available pre-scanning. if matches_excluded_path?(entry.path) - response.results.delete_if { |finding| finding.payload_id == entry.id } + response.results.delete_if { |finding| finding.blob_id == entry.id } findings_by_blobs.delete(entry.id) @@ -617,7 +596,7 @@ def transform_findings(response) end # Remove blobs that has already been found in a tree entry. - findings_by_blobs.delete_if { |payload_id, _| blobs_found_with_tree_entries.include?(payload_id) } + findings_by_blobs.delete_if { |blob_id, _| blobs_found_with_tree_entries.include?(blob_id) } # Return the findings as a hash sorted by commits and blobs (minus ones already found). { @@ -673,26 +652,14 @@ def send_request_to_sds(payloads, exclusions: {}) secret_detection_logger.error(message: e.message) end - # Expects an array of either Hashes or ScanRequest::Payloads - def build_payloads(data) - data.inject([]) do |payloads, datum| - payloads << build_payload(datum) + def build_sds_request(data, exclusions: {}, tags: []) + payloads = data.inject([]) do |payloads, datum| + payloads << ::Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( + id: datum[:id], + data: datum[:data] + ) end - end - # Expect `payload` is a hash or GRPC::ScanRequest::Payload object - def build_payload(datum) - return datum if datum.is_a?(Gitlab::SecretDetection::GRPC::ScanRequest::Payload) - - ::Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( - id: datum[:id], - data: datum[:data], - offset: datum.fetch(:offset, nil) - ) - end - - # Build the list of gRPC Exclusion objects - def build_exclusions(exclusions: {}) exclusion_ary = [] # exclusions are a hash of {string, array} pairs where the keys @@ -701,22 +668,13 @@ def build_exclusions(exclusions: {}) exclusions[key].inject(exclusion_ary) do |array, exclusion| type = EXCLUSION_TYPE_MAP[exclusion.type.to_sym] || UNKNOWN_EXCLUSION_TYPE - array << ::Gitlab::SecretDetection::GRPC::Exclusion.new( + array << ::Gitlab::SecretDetection::GRPC::ScanRequest::Exclusion.new( exclusion_type: type, value: exclusion.value ) end end - exclusion_ary - end - - # Puts the entire gRPC request object together - def build_sds_request(data, exclusions: {}, tags: []) - payloads = build_payloads(data) - - exclusion_ary = build_exclusions(exclusions:) - Gitlab::SecretDetection::GRPC::ScanRequest.new( payloads: payloads, exclusions: exclusion_ary, diff --git a/ee/spec/features/trials/lead_creation_form_validation_spec.rb b/ee/spec/features/trials/lead_creation_form_validation_spec.rb index 72661b85369211f321dc71f37801e26d71a95f01..7d09c63b6b22e5e309935a1ee3a5b1ad5b486991 100644 --- a/ee/spec/features/trials/lead_creation_form_validation_spec.rb +++ b/ee/spec/features/trials/lead_creation_form_validation_spec.rb @@ -27,6 +27,7 @@ "uid" => user.id, "setup_for_company" => user.setup_for_company, "skip_email_confirmation" => true, + "state" => "", "gitlab_com_trial" => true, "provider" => "gitlab" } diff --git a/ee/spec/frontend/trials/components/country_or_region_selector_spec.js b/ee/spec/frontend/trials/components/country_or_region_selector_spec.js index 55b964645e3ae70f0e98c343cea668a6084436c3..a5c3e78dccd01fcc6c98742c1e172e0015028544 100644 --- a/ee/spec/frontend/trials/components/country_or_region_selector_spec.js +++ b/ee/spec/frontend/trials/components/country_or_region_selector_spec.js @@ -57,27 +57,29 @@ describe('CountryOrRegionSelector', () => { wrapper = createComponent({ trackActionForErrors: '_trackActionForErrors_' }); }); - it('adds track error class for country selector', () => { + it('adds an error tracking class to the country and state selectors', () => { expect(findFormInput('country-dropdown').props('selectClass')).toContain('js-track-error'); + expect(findFormInput('state-dropdown').props('selectClass')).toContain('js-track-error'); }); }); }); describe.each` - country | display - ${'US'} | ${true} - ${'CA'} | ${true} - ${'NL'} | ${false} - `('Country & State handling', ({ country, display }) => { + country | hidden | required + ${'US'} | ${false} | ${'true'} + ${'CA'} | ${false} | ${'true'} + ${'NL'} | ${true} | ${undefined} + `('Country & State handling', ({ country, hidden, required }) => { describe(`when provided country is set to ${country}`, () => { beforeEach(() => { wrapper = createComponent({ country }); }); - it(`should${display ? '' : ' not'} render the state`, async () => { + it(`should${hidden ? ' not' : ''} render the state`, async () => { await nextTick(); - expect(findFormInput('state-dropdown').exists()).toBe(display); + expect(findFormInput('state-form-group').classes('gl-hidden')).toBe(hidden); + expect(findFormInput('state-dropdown').attributes('required')).toBe(required); }); }); }); diff --git a/ee/spec/lib/gitlab/checks/secrets_check_spec.rb b/ee/spec/lib/gitlab/checks/secrets_check_spec.rb index a632ed45d8b5aa73223868ecb1e171a3086755f6..b8197ae83c5b59951bde46cdeb4481651579b556 100644 --- a/ee/spec/lib/gitlab/checks/secrets_check_spec.rb +++ b/ee/spec/lib/gitlab/checks/secrets_check_spec.rb @@ -138,26 +138,4 @@ end end end - - describe '#get_project_security_exclusion_from_sds_exclusion' do - let_it_be(:project) { create(:project) } - let_it_be(:pse) { create(:project_security_exclusion, :with_rule, project: project) } - - let(:sds_exclusion) do - Gitlab::SecretDetection::GRPC::Exclusion.new( - exclusion_type: Gitlab::SecretDetection::GRPC::ExclusionType::EXCLUSION_TYPE_RULE, - value: pse.value - ) - end - - it 'returns the same object if it is a ProjectSecurityExclusion' do - result = secrets_check.send(:get_project_security_exclusion_from_sds_exclusion, pse) - expect(result).to be pse - end - - it 'returns the ProjectSecurityExclusion with the same value' do - result = secrets_check.send(:get_project_security_exclusion_from_sds_exclusion, sds_exclusion) - expect(result).to eq pse - end - end end diff --git a/ee/spec/support/shared_contexts/secrets_check_shared_contexts.rb b/ee/spec/support/shared_contexts/secrets_check_shared_contexts.rb index e6edb0f6df49b01d1db9fa7977248587900b181a..66785fd33094bfd361b3fbe1a02c8ab54340aaaa 100644 --- a/ee/spec/support/shared_contexts/secrets_check_shared_contexts.rb +++ b/ee/spec/support/shared_contexts/secrets_check_shared_contexts.rb @@ -34,21 +34,8 @@ let(:existing_blob) { have_attributes(class: Gitlab::Git::Blob, id: existing_blob_reference, size: 23) } let(:new_blob) { have_attributes(class: Gitlab::Git::Blob, id: new_blob_reference, size: 24) } - let(:existing_payload) do - Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( - id: existing_blob_reference, - data: "Documentation goes here", - offset: 1 - ) - end - - let(:new_payload) do - Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( - id: new_blob_reference, - data: "BASE_URL=https://foo.bar", - offset: 1 - ) - end + let(:existing_payload) { { id: existing_blob_reference, data: "Documentation goes here", offset: 1 } } + let(:new_payload) { { id: new_blob_reference, data: "BASE_URL=https://foo.bar", offset: 1 } } let(:changes) do [ @@ -169,11 +156,11 @@ # Error messsages with formatting let(:failed_to_scan_regex_error) do - format(error_messages[:failed_to_scan_regex_error], { payload_id: failed_to_scan_blob_reference }) + format(error_messages[:failed_to_scan_regex_error], { blob_id: failed_to_scan_blob_reference }) end let(:blob_timed_out_error) do - format(error_messages[:blob_timed_out_error], { payload_id: timed_out_blob_reference }) + format(error_messages[:blob_timed_out_error], { blob_id: timed_out_blob_reference }) end let(:too_many_tree_entries_error) do @@ -283,7 +270,7 @@ format( log_messages[:finding_message], { - payload_id: new_blob_reference, + blob_id: new_blob_reference, line_number: finding_line_number, description: finding_description } diff --git a/ee/spec/support/shared_examples/lib/gitlab/secrets_check_shared_examples.rb b/ee/spec/support/shared_examples/lib/gitlab/secrets_check_shared_examples.rb index f7f2c618ff8efb19f08f9aee49a46de512be790c..b9bd7f35330b32ea5995bdeaa25cf1a591ff520f 100644 --- a/ee/spec/support/shared_examples/lib/gitlab/secrets_check_shared_examples.rb +++ b/ee/spec/support/shared_examples/lib/gitlab/secrets_check_shared_examples.rb @@ -48,14 +48,13 @@ let(:payload) do ::Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( id: 'da66bef46dbf0ad7fdcbeec97c9eaa24c2846dda', - data: 'BASE_URL=https://foo.bar', - offset: 1 + data: 'BASE_URL=https://foo.bar' ) end let(:exclusion) do - ::Gitlab::SecretDetection::GRPC::Exclusion.new( - exclusion_type: ::Gitlab::SecretDetection::GRPC::ExclusionType::EXCLUSION_TYPE_PATH, + ::Gitlab::SecretDetection::GRPC::ScanRequest::Exclusion.new( + exclusion_type: ::Gitlab::SecretDetection::GRPC::ScanRequest::ExclusionType::EXCLUSION_TYPE_RULE, value: 'file-exclusion-1.rb' ) end @@ -123,11 +122,7 @@ RSpec.shared_examples 'entire file scan passed' do include_context 'secrets check context' - let(:passed_scan_response) do - ::Gitlab::SecretDetection::Core::Response.new( - status: Gitlab::SecretDetection::Core::Status::NOT_FOUND - ) - end + let(:passed_scan_response) { ::Gitlab::SecretDetection::Response.new(Gitlab::SecretDetection::Status::NOT_FOUND) } context 'with quarantine directory' do include_context 'quarantine directory exists' @@ -192,7 +187,7 @@ end it 'scans diffs' do - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( [new_payload], @@ -222,19 +217,8 @@ RSpec.shared_examples 'diff scan passed' do include_context 'secrets check context' - let(:passed_scan_response) do - ::Gitlab::SecretDetection::Core::Response.new( - status: ::Gitlab::SecretDetection::Core::Status::NOT_FOUND - ) - end - - let(:new_payload) do - Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( - id: new_blob_reference, - data: "BASE_URL=https://foo.bar", - offset: 1 - ) - end + let(:passed_scan_response) { ::Gitlab::SecretDetection::Response.new(Gitlab::SecretDetection::Status::NOT_FOUND) } + let(:new_payload) { { id: new_blob_reference, data: "BASE_URL=https://foo.bar", offset: 1 } } let(:diff_blob) do Gitlab::GitalyClient::DiffBlob.new( @@ -269,7 +253,7 @@ end it 'scans diffs' do - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( [new_payload], @@ -300,12 +284,12 @@ include_context 'secrets check context' let(:successful_scan_response) do - ::Gitlab::SecretDetection::Core::Response.new( - status: ::Gitlab::SecretDetection::Core::Status::FOUND, - results: [ - Gitlab::SecretDetection::Core::Finding.new( + ::Gitlab::SecretDetection::Response.new( + Gitlab::SecretDetection::Status::FOUND, + [ + Gitlab::SecretDetection::Finding.new( new_blob_reference, - Gitlab::SecretDetection::Core::Status::FOUND, + Gitlab::SecretDetection::Status::FOUND, 1, "gitlab_personal_access_token", "GitLab Personal Access Token" @@ -316,11 +300,7 @@ let(:new_blob_reference) { 'fe29d93da4843da433e62711ace82db601eb4f8f' } let(:new_payload) do - ::Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( - data: "SECRET=glpat-JUST20LETTERSANDNUMB", # gitleaks:allow - id: new_blob_reference, - offset: 1 - ) + { data: "SECRET=glpat-JUST20LETTERSANDNUMB", id: new_blob_reference, offset: 1 } # gitleaks:allow end let(:new_blob) { have_attributes(class: Gitlab::Git::Blob, id: new_blob_reference, size: 33) } @@ -398,7 +378,7 @@ end it 'scans diffs' do - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( [new_payload], @@ -441,11 +421,7 @@ let(:new_blob_reference) { 'fe29d93da4843da433e62711ace82db601eb4f8f' } let(:new_payload) do - Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( - data: "SECRET=glpat-JUST20LETTERSANDNUMB", # gitleaks:allow - id: new_blob_reference, - offset: 1 - ) + { data: "SECRET=glpat-JUST20LETTERSANDNUMB", id: new_blob_reference, offset: 1 } # gitleaks:allow end let(:tree_entries) do @@ -488,20 +464,19 @@ end let(:successful_with_same_blob_in_multiple_commits_scan_response) do - ::Gitlab::SecretDetection::Core::Response.new( - status: Gitlab::SecretDetection::Core::Status::FOUND, - results: + ::Gitlab::SecretDetection::Response.new( + Gitlab::SecretDetection::Status::FOUND, [ - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( new_blob_reference, - Gitlab::SecretDetection::Core::Status::FOUND, + Gitlab::SecretDetection::Status::FOUND, 1, "gitlab_personal_access_token", "GitLab Personal Access Token" ), - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( new_blob_reference, - Gitlab::SecretDetection::Core::Status::FOUND, + Gitlab::SecretDetection::Status::FOUND, 1, "gitlab_personal_access_token", "GitLab Personal Access Token" @@ -517,7 +492,7 @@ end it 'displays the findings with their corresponding commit sha/file path' do - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( [new_payload], @@ -557,7 +532,7 @@ end it 'scans diffs' do - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( [new_payload], @@ -588,7 +563,7 @@ end it 'scans diffs' do - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( [new_payload], @@ -735,28 +710,24 @@ let(:new_blob_reference) { '59ef300b246861163ee1e2ab4146e16144e4770f' } let(:new_payload) do - Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( - data: "SECRET=glpat-JUST20LETTERSANDNUMB\nTOKEN=glpat-JUST20LETTERSANDNUMB", # gitleaks:allow - id: new_blob_reference, - offset: 1 - ) + { data: "SECRET=glpat-JUST20LETTERSANDNUMB\nTOKEN=glpat-JUST20LETTERSANDNUMB", # gitleaks:allow + id: new_blob_reference, offset: 1 } end let(:successful_with_multiple_findings_scan_response) do - ::Gitlab::SecretDetection::Core::Response.new( - status: Gitlab::SecretDetection::Core::Status::FOUND, - results: + ::Gitlab::SecretDetection::Response.new( + Gitlab::SecretDetection::Status::FOUND, [ - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( new_blob_reference, - Gitlab::SecretDetection::Core::Status::FOUND, + Gitlab::SecretDetection::Status::FOUND, 1, "gitlab_personal_access_token", "GitLab Personal Access Token" ), - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( new_blob_reference, - Gitlab::SecretDetection::Core::Status::FOUND, + Gitlab::SecretDetection::Status::FOUND, 2, "gitlab_personal_access_token", "GitLab Personal Access Token" @@ -766,7 +737,7 @@ end it 'displays all findings with their corresponding commit sha/filepath' do - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( [new_payload], @@ -802,30 +773,26 @@ let(:new_blob_reference) { '13a31e7c93bbe8781f341e24e8ef26ef717d0da2' } let(:new_payload) do - Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( - data: "SECRET=glpat-JUST20LETTERSANDNUMB;TOKEN=GR1348941JUST20LETTERSANDNUMB", # gitleaks:allow - id: new_blob_reference, - offset: 1 - ) + { data: "SECRET=glpat-JUST20LETTERSANDNUMB;TOKEN=GR1348941JUST20LETTERSANDNUMB", # gitleaks:allow + id: new_blob_reference, offset: 1 } end let(:second_finding_description) { 'GitLab Runner Registration Token' } let(:successful_with_multiple_findings_on_same_line_scan_response) do - ::Gitlab::SecretDetection::Core::Response.new( - status: Gitlab::SecretDetection::Core::Status::FOUND, - results: + ::Gitlab::SecretDetection::Response.new( + Gitlab::SecretDetection::Status::FOUND, [ - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( new_blob_reference, - Gitlab::SecretDetection::Core::Status::FOUND, + Gitlab::SecretDetection::Status::FOUND, 1, "gitlab_personal_access_token", "GitLab Personal Access Token" ), - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( new_blob_reference, - Gitlab::SecretDetection::Core::Status::FOUND, + Gitlab::SecretDetection::Status::FOUND, 1, "gitlab_runner_registration_token", "GitLab Runner Registration Token" @@ -835,7 +802,7 @@ end it 'displays the findings with their corresponding commit sha/filepath' do - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( [new_payload], @@ -872,36 +839,27 @@ let(:new_blob_reference2) { '5f571267577ed6e0b4b24fb87f7a8218d5912eb9' } let(:new_payload) do - Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( - data: "SECRET=glpat-JUST20LETTERSANDNUMB", # gitleaks:allow - id: new_blob_reference, - offset: 1 - ) + { data: "SECRET=glpat-JUST20LETTERSANDNUMB", id: new_blob_reference, offset: 1 } # gitleaks:allow end let(:new_payload2) do - Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( - data: "SECRET=glrt-JUST20LETTERSANDNUMB", # gitleaks:allow - id: new_blob_reference2, - offset: 1 - ) + { data: "SECRET=glrt-JUST20LETTERSANDNUMB", id: new_blob_reference2, offset: 1 } # gitleaks:allow end let(:successful_with_multiple_files_findings_scan_response) do - ::Gitlab::SecretDetection::Core::Response.new( - status: Gitlab::SecretDetection::Core::Status::FOUND, - results: + ::Gitlab::SecretDetection::Response.new( + Gitlab::SecretDetection::Status::FOUND, [ - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( new_blob_reference, - Gitlab::SecretDetection::Core::Status::FOUND, + Gitlab::SecretDetection::Status::FOUND, 1, "gitlab_personal_access_token", "GitLab Personal Access Token" ), - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( new_blob_reference2, - Gitlab::SecretDetection::Core::Status::FOUND, + Gitlab::SecretDetection::Status::FOUND, 1, "gitlab_runner_authentication_token", "GitLab Runner Authentication Token" @@ -911,7 +869,7 @@ end it 'displays all findings with their corresponding commit sha/filepath' do - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( array_including(new_payload, new_payload2), @@ -948,7 +906,7 @@ let(:category) { described_class.name } before do - allow_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + allow_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| allow(instance).to receive(:secrets_scan) .with( [new_payload], @@ -1116,24 +1074,20 @@ let(:new_blob_reference) { 'fe29d93da4843da433e62711ace82db601eb4f8f' } let(:new_payload) do - Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( - data: "SECRET=glpat-JUST20LETTERSANDNUMB", # gitleaks:allow - id: new_blob_reference, - offset: 1 - ) + { data: "SECRET=glpat-JUST20LETTERSANDNUMB", # gitleaks:allow + id: new_blob_reference, offset: 1 } end # The new commit must have a secret, so create a commit with one. let_it_be(:new_commit) { create_commit('.env' => 'SECRET=glpat-JUST20LETTERSANDNUMB') } # gitleaks:allow let(:successful_scan_response) do - ::Gitlab::SecretDetection::Core::Response.new( - status: Gitlab::SecretDetection::Core::Status::FOUND, - results: + ::Gitlab::SecretDetection::Response.new( + Gitlab::SecretDetection::Status::FOUND, [ - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( new_blob_reference, - Gitlab::SecretDetection::Core::Status::FOUND, + Gitlab::SecretDetection::Status::FOUND, 1, "gitlab_personal_access_token", "GitLab Personal Access Token" @@ -1168,7 +1122,7 @@ end it 'scans diffs' do - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( [new_payload], @@ -1199,16 +1153,8 @@ context 'when multiple hunks exist in a single diff patch leading to multiple payloads' do let(:new_payloads) do [ - ::Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( - data: "SECRET=glpat-JUST20LETTERSANDNUMB", # gitleaks:allow - id: new_blob_reference, - offset: 1 - ), - ::Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( - data: "TOKEN=glpat-JUST20LETTERSANDNUMB", # gitleaks:allow - id: new_blob_reference, - offset: 11 - ) + { data: "SECRET=glpat-JUST20LETTERSANDNUMB", id: new_blob_reference, offset: 1 }, # gitleaks:allow + { data: "TOKEN=glpat-JUST20LETTERSANDNUMB", id: new_blob_reference, offset: 11 } # gitleaks:allow ] end @@ -1228,20 +1174,19 @@ end let(:successful_scan_response) do - ::Gitlab::SecretDetection::Core::Response.new( - status: Gitlab::SecretDetection::Core::Status::FOUND, - results: + ::Gitlab::SecretDetection::Response.new( + Gitlab::SecretDetection::Status::FOUND, [ - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( new_blob_reference, - Gitlab::SecretDetection::Core::Status::FOUND, + Gitlab::SecretDetection::Status::FOUND, 1, "gitlab_personal_access_token", "GitLab Personal Access Token" ), - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( new_blob_reference, - Gitlab::SecretDetection::Core::Status::FOUND, + Gitlab::SecretDetection::Status::FOUND, 11, "gitlab_personal_access_token", "GitLab Personal Access Token" @@ -1283,7 +1228,7 @@ .and_return([diff_blob]) end - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( new_payloads, @@ -1344,7 +1289,7 @@ .and_return([diff_blob]) end - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( new_payloads, @@ -1378,24 +1323,23 @@ include_context 'secrets check context' let(:successful_scan_with_errors_response) do - ::Gitlab::SecretDetection::Core::Response.new( - status: Gitlab::SecretDetection::Core::Status::FOUND_WITH_ERRORS, - results: + ::Gitlab::SecretDetection::Response.new( + Gitlab::SecretDetection::Status::FOUND_WITH_ERRORS, [ - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( new_blob_reference, - Gitlab::SecretDetection::Core::Status::FOUND, + Gitlab::SecretDetection::Status::FOUND, 1, "gitlab_personal_access_token", "GitLab Personal Access Token" ), - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( timed_out_blob_reference, - Gitlab::SecretDetection::Core::Status::PAYLOAD_TIMEOUT + Gitlab::SecretDetection::Status::PAYLOAD_TIMEOUT ), - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( failed_to_scan_blob_reference, - Gitlab::SecretDetection::Core::Status::SCAN_ERROR + Gitlab::SecretDetection::Status::SCAN_ERROR ) ] ) @@ -1426,27 +1370,15 @@ let(:failed_to_scan_blob) { have_attributes(class: Gitlab::Git::Blob, id: failed_to_scan_blob_reference, size: 32) } let(:new_payload) do - Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( - data: "SECRET=glpat-JUST20LETTERSANDNUMB", # gitleaks:allow - id: new_blob_reference, - offset: 1 - ) + { data: "SECRET=glpat-JUST20LETTERSANDNUMB", id: new_blob_reference, offset: 1 } # gitleaks:allow end let(:timed_out_payload) do - Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( - data: "TOKEN=glpat-JUST20LETTERSANDNUMB", # gitleaks:allow - id: timed_out_blob_reference, - offset: 1 - ) + { data: "TOKEN=glpat-JUST20LETTERSANDNUMB", id: timed_out_blob_reference, offset: 1 } # gitleaks:allow end let(:failed_to_scan_payload) do - Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( - data: "GLPAT=glpat-JUST20LETTERSANDNUMB", # gitleaks:allow - id: failed_to_scan_blob_reference, - offset: 1 - ) + { data: "GLPAT=glpat-JUST20LETTERSANDNUMB", id: failed_to_scan_blob_reference, offset: 1 } # gitleaks:allow end # Used for the quarantine directory context below. @@ -1478,7 +1410,7 @@ .and_return([existing_blob, new_blob, timed_out_blob, failed_to_scan_blob]) .and_call_original - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( array_including(new_payload, timed_out_payload, failed_to_scan_payload), @@ -1505,7 +1437,7 @@ .and_return([new_blob, timed_out_blob, failed_to_scan_blob]) end - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( array_including(new_payload, timed_out_payload, failed_to_scan_payload), @@ -1536,7 +1468,7 @@ .and_return([new_blob, timed_out_blob, failed_to_scan_blob]) .and_call_original - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( array_including(new_payload, timed_out_payload, failed_to_scan_payload), @@ -1556,7 +1488,7 @@ end it 'scans diffs' do - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( array_including(new_payload, timed_out_payload, failed_to_scan_payload), @@ -1594,7 +1526,7 @@ end it 'loads tree entries of the new commit' do - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( array_including(new_payload, timed_out_payload, failed_to_scan_payload), @@ -1649,46 +1581,42 @@ let(:new_blob_reference) { '59ef300b246861163ee1e2ab4146e16144e4770f' } let(:new_payload) do - Gitlab::SecretDetection::GRPC::ScanRequest::Payload.new( - data: "SECRET=glpat-JUST20LETTERSANDNUMB\nTOKEN=glpat-JUST20LETTERSANDNUMB", # gitleaks:allow - id: new_blob_reference, - offset: 1 - ) + { data: "SECRET=glpat-JUST20LETTERSANDNUMB\nTOKEN=glpat-JUST20LETTERSANDNUMB", # gitleaks:allow + id: new_blob_reference, offset: 1 } end let(:successful_scan_with_multiple_findings_and_errors_response) do - ::Gitlab::SecretDetection::Core::Response.new( - status: Gitlab::SecretDetection::Core::Status::FOUND_WITH_ERRORS, - results: + ::Gitlab::SecretDetection::Response.new( + Gitlab::SecretDetection::Status::FOUND_WITH_ERRORS, [ - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( new_blob_reference, - Gitlab::SecretDetection::Core::Status::FOUND, + Gitlab::SecretDetection::Status::FOUND, 1, "gitlab_personal_access_token", "GitLab Personal Access Token" ), - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( new_blob_reference, - Gitlab::SecretDetection::Core::Status::FOUND, + Gitlab::SecretDetection::Status::FOUND, 2, "gitlab_personal_access_token", "GitLab Personal Access Token" ), - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( timed_out_blob_reference, - Gitlab::SecretDetection::Core::Status::PAYLOAD_TIMEOUT + Gitlab::SecretDetection::Status::PAYLOAD_TIMEOUT ), - Gitlab::SecretDetection::Core::Finding.new( + Gitlab::SecretDetection::Finding.new( failed_to_scan_blob_reference, - Gitlab::SecretDetection::Core::Status::SCAN_ERROR + Gitlab::SecretDetection::Status::SCAN_ERROR ) ] ) end it 'displays all findings with their corresponding commit sha/filepath' do - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .with( array_including(new_payload, timed_out_payload, failed_to_scan_payload), @@ -1730,12 +1658,12 @@ include_context 'secrets check context' let(:scan_timed_out_scan_response) do - ::Gitlab::SecretDetection::Core::Response.new(status: ::Gitlab::SecretDetection::Core::Status::SCAN_TIMEOUT) + ::Gitlab::SecretDetection::Response.new(Gitlab::SecretDetection::Status::SCAN_TIMEOUT) end it 'logs the error and passes the check' do # Mock the response to return a scan timed out status. - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .and_return(scan_timed_out_scan_response) end @@ -1754,15 +1682,10 @@ before do # Intentionally set `RULESET_FILE_PATH` to an incorrect path to cause error. - stub_const('::Gitlab::SecretDetection::Core::Ruleset::RULESET_FILE_PATH', 'gitleaks.toml') + stub_const('::Gitlab::SecretDetection::Scan::RULESET_FILE_PATH', 'gitleaks.toml') end it 'logs the error and passes the check' do - allow(TomlRB).to receive(:load_file).and_raise( - StandardError, - "No such file or directory @ rb_sysopen - gitleaks.toml" - ) - # File parsing error is written to the logger. expect(secret_detection_logger).to receive(:error) .once @@ -1784,12 +1707,12 @@ include_context 'secrets check context' let(:failed_with_invalid_input_response) do - ::Gitlab::SecretDetection::Core::Response.new(status: ::Gitlab::SecretDetection::Core::Status::INPUT_ERROR) + ::Gitlab::SecretDetection::Response.new(::Gitlab::SecretDetection::Status::INPUT_ERROR) end it 'logs the error and passes the check' do # Mock the response to return a scan invalid input status. - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .and_return(failed_with_invalid_input_response) end @@ -1806,16 +1729,12 @@ RSpec.shared_examples 'scan skipped due to invalid status' do include_context 'secrets check context' - let(:invalid_scan_status_code) { -1 } # doesn't exist in ::Gitlab::SecretDetection::Core::Status - let(:invalid_scan_status_code_response) do - ::Gitlab::SecretDetection::Core::Response.new( - status: invalid_scan_status_code - ) - end + let(:invalid_scan_status_code) { -1 } # doesn't exist in ::Gitlab::SecretDetection::Status + let(:invalid_scan_status_code_response) { ::Gitlab::SecretDetection::Response.new(invalid_scan_status_code) } it 'logs the error and passes the check' do # Mock the response to return a scan invalid status. - expect_next_instance_of(::Gitlab::SecretDetection::Core::Scanner) do |instance| + expect_next_instance_of(::Gitlab::SecretDetection::Scan) do |instance| expect(instance).to receive(:secrets_scan) .and_return(invalid_scan_status_code_response) end diff --git a/lib/api/entities/todo.rb b/lib/api/entities/todo.rb index cfa27d9e490d500476c85c7062aee6d352c6ad3e..4d4b12890c16aa3c9231148399ae3b2431b38ba1 100644 --- a/lib/api/entities/todo.rb +++ b/lib/api/entities/todo.rb @@ -23,6 +23,8 @@ class Todo < Grape::Entity expose :updated_at def todo_target_class(target_type) + # Ensure the `Key` type properly maps to the `SSHKey` entity + target_type = "SSHKey" if target_type == "Key" # false as second argument prevents looking up in module hierarchy # see also https://gitlab.com/gitlab-org/gitlab-foss/issues/59719 ::API::Entities.const_get(target_type, false) diff --git a/spec/frontend/fixtures/static/gl_field_errors.html b/spec/frontend/fixtures/static/gl_field_errors.html index 27b8506f11407a6573561128b5058cacc345a909..e2aa713c4797eec3ac7993372181eac9d8f7aefb 100644 --- a/spec/frontend/fixtures/static/gl_field_errors.html +++ b/spec/frontend/fixtures/static/gl_field_errors.html @@ -21,6 +21,9 @@ <textarea required title="Textarea is required">Textarea</textarea> </div> <div class="form-group"> +<select required><option value="">Select number</option><option value="1">1</option></select> +</div> +<div class="form-group"> <input type="text" title="xss:<script>alert(0)</script>"></input> </div> <div class="form-group"></div> diff --git a/spec/frontend/gl_field_errors_spec.js b/spec/frontend/gl_field_errors_spec.js index b22f53a550b42b91d70d11ee93a07620cf3008c7..7233d6f4b6e2b498f6c06d204b3ed77c14aa490a 100644 --- a/spec/frontend/gl_field_errors_spec.js +++ b/spec/frontend/gl_field_errors_spec.js @@ -28,7 +28,7 @@ describe('GL Style Field Errors', () => { expect(testContext.fieldErrors).toBeDefined(); const { inputs } = testContext.fieldErrors.state; - expect(inputs.length).toBe(6); + expect(inputs.length).toBe(7); }); it('should ignore elements with custom error handling', () => { @@ -45,9 +45,9 @@ describe('GL Style Field Errors', () => { }); it('should not show any errors before submit attempt', () => { - testContext.$form.find('.email').val('not-a-valid-email').keyup(); - testContext.$form.find('.text-required').val('').keyup(); - testContext.$form.find('.alphanumberic').val('?---*').keyup(); + testContext.$form.find('.email').val('not-a-valid-email').trigger('input'); + testContext.$form.find('.required-text').val('').trigger('input'); + testContext.$form.find('.alphanumberic').val('?---*').trigger('input'); const errorsShown = testContext.$form.find('.gl-field-error-outline'); @@ -55,15 +55,15 @@ describe('GL Style Field Errors', () => { }); it('should show errors when input valid is submitted', () => { - testContext.$form.find('.email').val('not-a-valid-email').keyup(); - testContext.$form.find('.text-required').val('').keyup(); - testContext.$form.find('.alphanumberic').val('?---*').keyup(); + testContext.$form.find('.email').val('not-a-valid-email').trigger('input'); + testContext.$form.find('.required-text').val('').trigger('input'); + testContext.$form.find('.alphanumberic').val('?---*').trigger('input'); testContext.$form.submit(); const errorsShown = testContext.$form.find('.gl-field-error-outline'); - expect(errorsShown.length).toBe(4); + expect(errorsShown.length).toBe(5); }); it('should properly track validity state on input after invalid submission attempt', () => { @@ -79,41 +79,61 @@ describe('GL Style Field Errors', () => { expect(fieldState.valid).toBe(false); // Then invalid input - emailInputElement.val('not-a-valid-email').keyup(); + emailInputElement.val('not-a-valid-email').trigger('input'); expect(emailInputElement).toHaveClass('gl-field-error-outline'); expect(fieldState.empty).toBe(false); expect(fieldState.valid).toBe(false); // Then valid input - emailInputElement.val('email@gitlab.com').keyup(); + emailInputElement.val('email@gitlab.com').trigger('input'); expect(emailInputElement).not.toHaveClass('gl-field-error-outline'); expect(fieldState.empty).toBe(false); expect(fieldState.valid).toBe(true); // Then invalid input - emailInputElement.val('not-a-valid-email').keyup(); + emailInputElement.val('not-a-valid-email').trigger('input'); expect(emailInputElement).toHaveClass('gl-field-error-outline'); expect(fieldState.empty).toBe(false); expect(fieldState.valid).toBe(false); // Then empty input - emailInputElement.val('').keyup(); + emailInputElement.val('').trigger('input'); expect(emailInputElement).toHaveClass('gl-field-error-outline'); expect(fieldState.empty).toBe(true); expect(fieldState.valid).toBe(false); // Then valid input - emailInputElement.val('email@gitlab.com').keyup(); + emailInputElement.val('email@gitlab.com').trigger('input'); expect(emailInputElement).not.toHaveClass('gl-field-error-outline'); expect(fieldState.empty).toBe(false); expect(fieldState.valid).toBe(true); }); + it('should properly track validity state on select input after invalid submission attempt', () => { + testContext.$form.submit(); + + const selectInputModel = testContext.fieldErrors.state.inputs[5]; + const fieldState = selectInputModel.state; + const selectInputElement = selectInputModel.inputElement; + + // No input + expect(selectInputElement).toHaveClass('gl-field-error-outline'); + expect(fieldState.empty).toBe(true); + expect(fieldState.valid).toBe(false); + + // Then valid input + selectInputElement.val('1').trigger('input'); + + expect(selectInputElement).not.toHaveClass('gl-field-error-outline'); + expect(fieldState.empty).toBe(false); + expect(fieldState.valid).toBe(true); + }); + it('should properly infer error messages', () => { testContext.$form.submit(); const trackedInputs = testContext.fieldErrors.state.inputs; @@ -130,7 +150,7 @@ describe('GL Style Field Errors', () => { testContext.$form.submit(); const trackedInputs = testContext.fieldErrors.state.inputs; - const xssInput = trackedInputs[5]; + const xssInput = trackedInputs[6]; const xssErrorElem = xssInput.inputElement.siblings('.gl-field-error'); diff --git a/spec/lib/api/entities/todo_spec.rb b/spec/lib/api/entities/todo_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..4cc2fab10fc079c994ddef953d7d5bec8035dc69 --- /dev/null +++ b/spec/lib/api/entities/todo_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::API::Entities::Todo, feature_category: :notifications do + using RSpec::Parameterized::TableSyntax + + let(:todo) { build_stubbed(:todo) } + + subject(:entity) { described_class.new(todo) } + + describe '#todo_target_class' do + where(:type, :expected_entity) do + "Issue" | API::Entities::Issue + "Namespace" | API::Entities::Namespace + "Key" | API::Entities::SSHKey + end + + with_them do + it "maps the type to the correct API entity" do + expect(entity.todo_target_class(type)).to be(expected_entity) + end + end + end +end