diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-12 21:09:45 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-12 21:09:45 +0000 |
commit | dd4bee69b7d55620f7dc9db8c36b478bd4959755 (patch) | |
tree | 78ba4c486ad8aa2d5effaccf23241ffb6c6dde26 | |
parent | ce8a0b90849ac5d1895e741c023432930f24d724 (diff) | |
download | gitlab-ce-dd4bee69b7d55620f7dc9db8c36b478bd4959755.tar.gz |
Add latest changes from gitlab-org/gitlab@master
33 files changed, 318 insertions, 95 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index fa87b981f5d..21a376c4885 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -10,17 +10,24 @@ *.md @gl-docsteam # Frontend maintainers should see everything in `app/assets/` -app/assets/ @gitlab-org/maintainers/frontend *.scss @annabeldunstone @gitlab-org/maintainers/frontend -/scripts/frontend/ @gitlab-org/maintainers/frontend +*.js @gitlab-org/maintainers/frontend +/app/assets/ @gitlab-org/maintainers/frontend +/ee/app/assets/ @gitlab-org/maintainers/frontend +/spec/javascripts/ @gitlab-org/maintainers/frontend +/ee/spec/javascripts/ @gitlab-org/maintainers/frontend +/spec/frontend/ @gitlab-org/maintainers/frontend +/ee/spec/frontend/ @gitlab-org/maintainers/frontend # Database maintainers should review changes in `db/` -db/ @gitlab-org/maintainers/database -lib/gitlab/background_migration/ @gitlab-org/maintainers/database -lib/gitlab/database/ @gitlab-org/maintainers/database -lib/gitlab/sql/ @gitlab-org/maintainers/database -lib/gitlab/github_import/ @gitlab-org/maintainers/database +/db/ @gitlab-org/maintainers/database /ee/db/ @gitlab-org/maintainers/database +/lib/gitlab/background_migration/ @gitlab-org/maintainers/database +/ee/lib/ee/gitlab/background_migration/ @gitlab-org/maintainers/database +/lib/gitlab/database/ @gitlab-org/maintainers/database +/ee/lib/gitlab/database/ @gitlab-org/maintainers/database +/lib/gitlab/sql/ @gitlab-org/maintainers/database +/lib/gitlab/github_import/ @gitlab-org/maintainers/database /app/finders/ @gitlab-org/maintainers/database /ee/app/finders/ @gitlab-org/maintainers/database @@ -40,14 +47,14 @@ lib/gitlab/github_import/ @gitlab-org/maintainers/database # Engineering Productivity owned files /.gitlab-ci.yml @gl-quality/eng-prod /.gitlab/ci/ @gl-quality/eng-prod +/.gitlab/ci/docs.gitlab-ci.yml @gl-quality/eng-prod @gl-docsteam +/.gitlab/ci/releases.gitlab-ci.yml @gl-quality/eng-prod @gitlab-org/delivery /.gitlab/CODEOWNERS @gl-quality/eng-prod Dangerfile @gl-quality/eng-prod /danger/ @gl-quality/eng-prod /lib/gitlab/danger/ @gl-quality/eng-prod /scripts/ @gl-quality/eng-prod - -# Delivery owner files -/.gitlab/ci/releases.gitlab-ci.yml @gitlab-org/delivery +/scripts/frontend/ @gl-quality/eng-prod @gitlab-org/maintainers/frontend # Telemetry owner files /ee/lib/gitlab/usage_data_counters/ @gitlab-org/growth/telemetry diff --git a/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue b/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue index d304ae7dbf6..7f0c232eea8 100644 --- a/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue +++ b/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue @@ -4,6 +4,9 @@ import Cookies from 'js-cookie'; import { parseBoolean, scrollToElement } from '~/lib/utils/common_utils'; import { s__ } from '~/locale'; import { glEmojiTag } from '~/emoji'; +import Tracking from '~/tracking'; + +const trackingMixin = Tracking.mixin(); const popoverStates = { suggest_gitlab_ci_yml: { @@ -27,6 +30,7 @@ export default { GlIcon, GlButton, }, + mixins: [trackingMixin], props: { target: { type: String, @@ -40,10 +44,18 @@ export default { type: String, required: true, }, + humanAccess: { + type: String, + required: true, + }, }, data() { return { popoverDismissed: parseBoolean(Cookies.get(this.dismissKey)), + tracking: { + label: this.trackLabel, + property: this.humanAccess, + }, }; }, computed: { @@ -60,12 +72,17 @@ export default { mounted() { if (this.trackLabel === 'suggest_commit_first_project_gitlab_ci_yml' && !this.popoverDismissed) scrollToElement(document.querySelector(this.target)); + + this.trackOnShow(); }, methods: { onDismiss() { this.popoverDismissed = true; Cookies.set(this.dismissKey, this.popoverDismissed, { expires: 365 }); }, + trackOnShow() { + if (!this.popoverDismissed) this.track(); + }, }, }; </script> diff --git a/app/assets/javascripts/blob/suggest_gitlab_ci_yml/index.js b/app/assets/javascripts/blob/suggest_gitlab_ci_yml/index.js index 2cc342fb381..3b67b3dd259 100644 --- a/app/assets/javascripts/blob/suggest_gitlab_ci_yml/index.js +++ b/app/assets/javascripts/blob/suggest_gitlab_ci_yml/index.js @@ -10,6 +10,7 @@ export default el => target: el.dataset.target, trackLabel: el.dataset.trackLabel, dismissKey: el.dataset.dismissKey, + humanAccess: el.dataset.humanAccess, }, }); }, diff --git a/app/assets/javascripts/ide/components/preview/clientside.vue b/app/assets/javascripts/ide/components/preview/clientside.vue index beb179d0411..aa8d932da6e 100644 --- a/app/assets/javascripts/ide/components/preview/clientside.vue +++ b/app/assets/javascripts/ide/components/preview/clientside.vue @@ -21,7 +21,7 @@ export default { }; }, computed: { - ...mapState(['entries', 'promotionSvgPath', 'links']), + ...mapState(['entries', 'promotionSvgPath', 'links', 'codesandboxBundlerUrl']), ...mapGetters(['packageJson', 'currentProject']), normalizedEntries() { return Object.keys(this.entries).reduce((acc, path) => { @@ -106,12 +106,7 @@ export default { return this.loadFileContent(this.mainEntry) .then(() => this.$nextTick()) .then(() => { - this.initManager('#ide-preview', this.sandboxOpts, { - fileResolver: { - isFile: p => Promise.resolve(Boolean(this.entries[createPathWithExt(p)])), - readFile: p => this.loadFileContent(createPathWithExt(p)).then(content => content), - }, - }); + this.initManager(); this.listener = listen(e => { switch (e.type) { @@ -139,8 +134,18 @@ export default { this.manager.updatePreview(this.sandboxOpts); }, 250); }, - initManager(el, opts, resolver) { - this.manager = new Manager(el, opts, resolver); + initManager() { + const { codesandboxBundlerUrl: bundlerURL } = this; + + const settings = { + fileResolver: { + isFile: p => Promise.resolve(Boolean(this.entries[createPathWithExt(p)])), + readFile: p => this.loadFileContent(createPathWithExt(p)).then(content => content), + }, + ...(bundlerURL ? { bundlerURL } : {}), + }; + + this.manager = new Manager('#ide-preview', this.sandboxOpts, settings); }, }, }; diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js index a3450522697..9e9d9df8f82 100644 --- a/app/assets/javascripts/ide/index.js +++ b/app/assets/javascripts/ide/index.js @@ -53,6 +53,7 @@ export function initIde(el, options = {}) { clientsidePreviewEnabled: parseBoolean(el.dataset.clientsidePreviewEnabled), renderWhitespaceInCode: parseBoolean(el.dataset.renderWhitespaceInCode), editorTheme: window.gon?.user_color_scheme || DEFAULT_THEME, + codesandboxBundlerUrl: el.dataset.codesandboxBundlerUrl, }); }, methods: { diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js index a714562c5b2..0fd6a448283 100644 --- a/app/assets/javascripts/ide/stores/state.js +++ b/app/assets/javascripts/ide/stores/state.js @@ -34,4 +34,5 @@ export default () => ({ clientsidePreviewEnabled: false, renderWhitespaceInCode: false, editorTheme: DEFAULT_THEME, + codesandboxBundlerUrl: null, }); diff --git a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue index 018b30d2a67..05ad7b4ea3e 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue @@ -1,6 +1,6 @@ <script> import $ from 'jquery'; -import _ from 'underscore'; +import { intersection } from 'lodash'; import '~/smart_interval'; @@ -38,7 +38,7 @@ export default { } else { changedCommands = []; } - if (changedCommands && _.intersection(subscribedCommands, changedCommands).length) { + if (changedCommands && intersection(subscribedCommands, changedCommands).length) { this.mediator.fetch(); } }, diff --git a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js index 66d1fed7d31..2a61f7b5c05 100644 --- a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js +++ b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js @@ -1,6 +1,6 @@ import $ from 'jquery'; import '~/gl_dropdown'; -import _ from 'underscore'; +import { escape as esc } from 'lodash'; import { __ } from '~/locale'; function isValidProjectId(id) { @@ -49,7 +49,7 @@ class SidebarMoveIssue { renderRow: project => ` <li> <a href="#" class="js-move-issue-dropdown-item"> - ${_.escape(project.name_with_namespace)} + ${esc(project.name_with_namespace)} </a> </li> `, diff --git a/app/controllers/concerns/clientside_preview_csp.rb b/app/controllers/concerns/clientside_preview_csp.rb new file mode 100644 index 00000000000..6892c441b67 --- /dev/null +++ b/app/controllers/concerns/clientside_preview_csp.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module ClientsidePreviewCSP + extend ActiveSupport::Concern + + included do + content_security_policy do |p| + next if p.directives.blank? + next unless Gitlab::CurrentSettings.web_ide_clientside_preview_enabled? + + default_frame_src = p.directives['frame-src'] || p.directives['default-src'] + frame_src_values = Array.wrap(default_frame_src) | [Gitlab::CurrentSettings.web_ide_clientside_preview_bundler_url].compact + + p.frame_src(*frame_src_values) + end + end +end diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb index d94f18beb5d..8a838db04f9 100644 --- a/app/controllers/ide_controller.rb +++ b/app/controllers/ide_controller.rb @@ -3,6 +3,7 @@ class IdeController < ApplicationController layout 'fullscreen' + include ClientsidePreviewCSP include StaticObjectExternalStorageCSP def index diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 9c09ddafbf1..cc5ae32856a 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -353,4 +353,8 @@ module BlobHelper def suggest_pipeline_commit_cookie_name "suggest_gitlab_ci_yml_commit_#{@project.id}" end + + def human_access + @project.team.human_max_access(current_user&.id).try(:downcase) + end end diff --git a/app/helpers/ide_helper.rb b/app/helpers/ide_helper.rb index e4d0e605254..d6145493ba6 100644 --- a/app/helpers/ide_helper.rb +++ b/app/helpers/ide_helper.rb @@ -10,8 +10,9 @@ module IdeHelper "promotion-svg-path": image_path('illustrations/web-ide_promotion.svg'), "ci-help-page-path" => help_page_path('ci/quick_start/README'), "web-ide-help-page-path" => help_page_path('user/project/web_ide/index.html'), - "clientside-preview-enabled": Gitlab::CurrentSettings.current_application_settings.web_ide_clientside_preview_enabled.to_s, - "render-whitespace-in-code": current_user.render_whitespace_in_code.to_s + "clientside-preview-enabled": Gitlab::CurrentSettings.web_ide_clientside_preview_enabled?.to_s, + "render-whitespace-in-code": current_user.render_whitespace_in_code.to_s, + "codesandbox-bundler-url": Gitlab::CurrentSettings.web_ide_clientside_preview_bundler_url } end end diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb index 3f2106c80bf..d1a919fc01a 100644 --- a/app/models/application_setting_implementation.rb +++ b/app/models/application_setting_implementation.rb @@ -351,6 +351,12 @@ module ApplicationSettingImplementation static_objects_external_storage_url.present? end + # This will eventually be configurable + # https://gitlab.com/gitlab-org/gitlab/issues/208161 + def web_ide_clientside_preview_bundler_url + 'https://sandbox-prod.gitlab-static.net' + end + private def separate_whitelists(string_array) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index c6481bdbb6c..5436f034657 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -257,7 +257,7 @@ class WikiPage def title_changed? if persisted? old_title, old_dir = wiki.page_title_and_dir(self.class.unhyphenize(@page.url_path)) - new_title, new_dir = wiki.page_title_and_dir(title) + new_title, new_dir = wiki.page_title_and_dir(self.class.unhyphenize(title)) new_title != old_title || (title.include?('/') && new_dir != old_dir) else diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml index cd8407608a3..51b0b2722d1 100644 --- a/app/views/projects/blob/_editor.html.haml +++ b/app/views/projects/blob/_editor.html.haml @@ -23,7 +23,8 @@ .js-suggest-gitlab-ci-yml{ data: { toggle: 'popover', target: '#gitlab-ci-yml-selector', track_label: 'suggest_gitlab_ci_yml', - dismiss_key: "suggest_gitlab_ci_yml_#{@project.id}" } } + dismiss_key: "suggest_gitlab_ci_yml_#{@project.id}", + human_access: human_access } } .file-buttons - if is_markdown diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index bf7900018c3..1afbe1fe24e 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -17,4 +17,5 @@ .js-suggest-gitlab-ci-yml-commit-changes{ data: { toggle: 'popover', target: '#commit-changes', track_label: 'suggest_commit_first_project_gitlab_ci_yml', - dismiss_key: "suggest_commit_first_project_gitlab_ci_yml_#{@project.id}" } } + dismiss_key: "suggest_commit_first_project_gitlab_ci_yml_#{@project.id}", + human_access: human_access } } diff --git a/changelogs/unreleased/10526-smartcard_support_different_hostname.yml b/changelogs/unreleased/10526-smartcard_support_different_hostname.yml new file mode 100644 index 00000000000..6990449d171 --- /dev/null +++ b/changelogs/unreleased/10526-smartcard_support_different_hostname.yml @@ -0,0 +1,5 @@ +--- +title: Make hostname configurable for smartcard authentication +merge_request: 26411 +author: +type: added diff --git a/changelogs/unreleased/207623-fix-code-search-pagination.yml b/changelogs/unreleased/207623-fix-code-search-pagination.yml new file mode 100644 index 00000000000..7cb90f0bef4 --- /dev/null +++ b/changelogs/unreleased/207623-fix-code-search-pagination.yml @@ -0,0 +1,5 @@ +--- +title: Fix code search pagination on a custom branch +merge_request: 25984 +author: +type: fixed diff --git a/changelogs/unreleased/209761-fix-wiki-directories-with-hyphens.yml b/changelogs/unreleased/209761-fix-wiki-directories-with-hyphens.yml new file mode 100644 index 00000000000..3a18143ba7a --- /dev/null +++ b/changelogs/unreleased/209761-fix-wiki-directories-with-hyphens.yml @@ -0,0 +1,5 @@ +--- +title: Fix WikiPage#title_changed for paths with spaces +merge_request: 27087 +author: +type: fixed diff --git a/changelogs/unreleased/27144-gitlab-hosted-codesandbox.yml b/changelogs/unreleased/27144-gitlab-hosted-codesandbox.yml new file mode 100644 index 00000000000..bb7e011f94c --- /dev/null +++ b/changelogs/unreleased/27144-gitlab-hosted-codesandbox.yml @@ -0,0 +1,5 @@ +--- +title: Update Web IDE clientside preview bundler to use GitLab managed server +merge_request: 21520 +author: +type: changed diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 760688d8088..81085d4641e 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -752,7 +752,9 @@ production: &base # Path to a file containing a CA certificate ca_file: '/etc/ssl/certs/CA.pem' - # Port where the client side certificate is requested by the webserver (NGINX/Apache) + # Host and port where the client side certificate is requested by the + # webserver (NGINX/Apache) + # client_certificate_required_host: smartcard.gitlab.example.com # client_certificate_required_port: 3444 # Browser session with smartcard sign-in is required for Git access diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index f22ddc7f081..1b368346cfa 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -77,6 +77,7 @@ end Gitlab.ee do Settings['smartcard'] ||= Settingslogic.new({}) Settings.smartcard['enabled'] = false if Settings.smartcard['enabled'].nil? + Settings.smartcard['client_certificate_required_host'] = Settings.gitlab['host'] if Settings.smartcard['client_certificate_required_host'].nil? Settings.smartcard['client_certificate_required_port'] = 3444 if Settings.smartcard['client_certificate_required_port'].nil? Settings.smartcard['required_for_git_access'] = false if Settings.smartcard['required_for_git_access'].nil? Settings.smartcard['san_extensions'] = false if Settings.smartcard['san_extensions'].nil? diff --git a/package.json b/package.json index f8f00b67f6b..fa359867ea2 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "chart.js": "2.7.2", "classlist-polyfill": "^1.2.0", "clipboard": "^1.7.1", - "codesandbox-api": "^0.0.20", + "codesandbox-api": "0.0.23", "compression-webpack-plugin": "^3.0.1", "copy-webpack-plugin": "^5.0.5", "core-js": "^3.6.4", @@ -110,7 +110,7 @@ "raw-loader": "^4.0.0", "sanitize-html": "^1.22.0", "select2": "3.5.2-browserify", - "smooshpack": "^0.0.54", + "smooshpack": "^0.0.62", "sortablejs": "^1.10.2", "sql.js": "^0.4.0", "stickyfilljs": "^2.1.0", diff --git a/scripts/lint-doc.sh b/scripts/lint-doc.sh index d9440a5f052..d9de48fd0c0 100755 --- a/scripts/lint-doc.sh +++ b/scripts/lint-doc.sh @@ -2,36 +2,41 @@ cd "$(dirname "$0")/.." echo "=> Linting documents at path $(pwd) as $(whoami)..." +echo +ERRORCODE=0 # Use long options (e.g. --header instead of -H) for curl examples in documentation. echo '=> Checking for cURL short options...' +echo grep --extended-regexp --recursive --color=auto 'curl (.+ )?-[^- ].*' doc/ >/dev/null 2>&1 if [ $? -eq 0 ] then echo '✖ ERROR: Short options for curl should not be used in documentation! Use long options (e.g., --header instead of -H):' >&2 grep --extended-regexp --recursive --color=auto 'curl (.+ )?-[^- ].*' doc/ - exit 1 + ((ERRORCODE++)) fi # Ensure that the CHANGELOG.md does not contain duplicate versions DUPLICATE_CHANGELOG_VERSIONS=$(grep --extended-regexp '^## .+' CHANGELOG.md | sed -E 's| \(.+\)||' | sort -r | uniq -d) echo '=> Checking for CHANGELOG.md duplicate entries...' +echo if [ "${DUPLICATE_CHANGELOG_VERSIONS}" != "" ] then echo '✖ ERROR: Duplicate versions in CHANGELOG.md:' >&2 echo "${DUPLICATE_CHANGELOG_VERSIONS}" >&2 - exit 1 + ((ERRORCODE++)) fi # Make sure no files in doc/ are executable EXEC_PERM_COUNT=$(find doc/ -type f -perm 755 | wc -l) echo "=> Checking $(pwd)/doc for executable permissions..." +echo if [ "${EXEC_PERM_COUNT}" -ne 0 ] then echo '✖ ERROR: Executable permissions should not be used in documentation! Use `chmod 644` to the files in question:' >&2 find doc/ -type f -perm 755 - exit 1 + ((ERRORCODE++)) fi # Do not use 'README.md', instead use 'index.md' @@ -39,13 +44,14 @@ fi NUMBER_READMES=46 FIND_READMES=$(find doc/ -name "README.md" | wc -l) echo '=> Checking for new README.md files...' +echo if [ ${FIND_READMES} -ne $NUMBER_READMES ] then echo echo ' ✖ ERROR: New README.md file(s) detected, prefer index.md over README.md.' >&2 echo ' https://docs.gitlab.com/ee/development/documentation/styleguide.html#work-with-directories-and-files' echo - exit 1 + ((ERRORCODE++)) fi MD_DOC_PATH=${MD_DOC_PATH:-doc} @@ -64,7 +70,7 @@ function run_locally_or_in_docker() { echo echo " ✖ ERROR: '${cmd}' not found. Install '${cmd}' or Docker to proceed." >&2 echo - exit 1 + ((ERRORCODE++)) fi if [ $? -ne 0 ] @@ -72,15 +78,22 @@ function run_locally_or_in_docker() { echo echo " ✖ ERROR: '${cmd}' failed with errors." >&2 echo - exit 1 + ((ERRORCODE++)) fi } echo '=> Linting markdown style...' +echo run_locally_or_in_docker 'markdownlint' "--config .markdownlint.json ${MD_DOC_PATH}" echo '=> Linting prose...' run_locally_or_in_docker 'vale' "--minAlertLevel error ${MD_DOC_PATH}" -echo "✔ Linting passed" -exit 0 +if [ $ERRORCODE -ne 0 ] +then + echo "✖ ${ERRORCODE} lint test(s) failed. Review the log carefully to see full listing." + exit 1 +else + echo "✔ Linting passed" + exit 0 +fi diff --git a/scripts/security-harness b/scripts/security-harness index a1642489fe2..a0fa57ef042 100755 --- a/scripts/security-harness +++ b/scripts/security-harness @@ -28,9 +28,9 @@ HOOK_DATA = <<~HOOK if [ -e "$harness" ] then - if [[ ("$url" != *"dev.gitlab.org"*) && ("$url" != *"gitlab-org/security/"*) ]] + if [["$url" != *"gitlab-org/security/"*]] then - echo "Pushing to remotes other than dev.gitlab.org and gitlab.com/gitlab-org/security has been disabled!" + echo "Pushing to remotes other than gitlab.com/gitlab-org/security has been disabled!" echo "Run scripts/security-harness to disable this check." echo @@ -58,7 +58,7 @@ def toggle else FileUtils.touch(harness_path) - puts "#{SHELL_GREEN}Security harness installed -- you will only be able to push to dev.gitlab.org or gitlab.com/gitlab-org/security!#{SHELL_CLEAR}" + puts "#{SHELL_GREEN}Security harness installed -- you will only be able to push to gitlab.com/gitlab-org/security!#{SHELL_CLEAR}" end end diff --git a/spec/features/ide/clientside_preview_csp_spec.rb b/spec/features/ide/clientside_preview_csp_spec.rb new file mode 100644 index 00000000000..e097513def3 --- /dev/null +++ b/spec/features/ide/clientside_preview_csp_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'IDE Clientside Preview CSP' do + let_it_be(:user) { create(:user) } + + shared_context 'disable feature' do + before do + allow_next_instance_of(ApplicationSetting) do |instance| + allow(instance).to receive(:web_ide_clientside_preview_enabled?).and_return(false) + end + end + end + + it_behaves_like 'setting CSP', 'frame-src' do + let(:whitelisted_url) { 'https://sandbox.gitlab-static.test' } + let(:extended_controller_class) { IdeController } + + subject do + visit ide_path + + response_headers['Content-Security-Policy'] + end + + before do + allow_next_instance_of(ApplicationSetting) do |instance| + allow(instance).to receive(:web_ide_clientside_preview_enabled?).and_return(true) + allow(instance).to receive(:web_ide_clientside_preview_bundler_url).and_return(whitelisted_url) + end + + sign_in(user) + end + end +end diff --git a/spec/features/ide/static_object_external_storage_csp_spec.rb b/spec/features/ide/static_object_external_storage_csp_spec.rb index 93c22b35786..739b3fe2471 100644 --- a/spec/features/ide/static_object_external_storage_csp_spec.rb +++ b/spec/features/ide/static_object_external_storage_csp_spec.rb @@ -11,7 +11,7 @@ describe 'Static Object External Storage Content Security Policy' do end end - it_behaves_like 'setting CSP connect-src' do + it_behaves_like 'setting CSP', 'connect-src' do let_it_be(:whitelisted_url) { 'https://static-objects.test' } let_it_be(:extended_controller_class) { IdeController } diff --git a/spec/features/projects/sourcegraph_csp_spec.rb b/spec/features/projects/sourcegraph_csp_spec.rb index 385a797368c..f252d3cd027 100644 --- a/spec/features/projects/sourcegraph_csp_spec.rb +++ b/spec/features/projects/sourcegraph_csp_spec.rb @@ -12,7 +12,7 @@ describe 'Sourcegraph Content Security Policy' do end end - it_behaves_like 'setting CSP connect-src' do + it_behaves_like 'setting CSP', 'connect-src' do let_it_be(:whitelisted_url) { 'https://sourcegraph.test' } let_it_be(:extended_controller_class) { Projects::BlobController } diff --git a/spec/frontend/blob/suggest_gitlab_ci_yml/components/popover_spec.js b/spec/frontend/blob/suggest_gitlab_ci_yml/components/popover_spec.js index 2c7891e4b1a..43e92bdca5f 100644 --- a/spec/frontend/blob/suggest_gitlab_ci_yml/components/popover_spec.js +++ b/spec/frontend/blob/suggest_gitlab_ci_yml/components/popover_spec.js @@ -1,6 +1,7 @@ import { shallowMount } from '@vue/test-utils'; import Popover from '~/blob/suggest_gitlab_ci_yml/components/popover.vue'; import Cookies from 'js-cookie'; +import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; import * as utils from '~/lib/utils/common_utils'; jest.mock('~/lib/utils/common_utils', () => ({ @@ -11,6 +12,8 @@ jest.mock('~/lib/utils/common_utils', () => ({ const target = 'gitlab-ci-yml-selector'; const dismissKey = 'suggest_gitlab_ci_yml_99'; const defaultTrackLabel = 'suggest_gitlab_ci_yml'; +const commitTrackLabel = 'suggest_commit_first_project_gitlab_ci_yml'; +const humanAccess = 'owner'; describe('Suggest gitlab-ci.yml Popover', () => { let wrapper; @@ -21,6 +24,7 @@ describe('Suggest gitlab-ci.yml Popover', () => { target, trackLabel, dismissKey, + humanAccess, }, }); } @@ -50,15 +54,43 @@ describe('Suggest gitlab-ci.yml Popover', () => { expect(wrapper.vm.popoverDismissed).toEqual(true); }); - beforeEach(() => { + afterEach(() => { Cookies.remove(dismissKey); }); }); + describe('tracking', () => { + let trackingSpy; + + beforeEach(() => { + createWrapper(commitTrackLabel); + trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn); + }); + + afterEach(() => { + unmockTracking(); + }); + + it('sends a tracking event with the expected properties for the popover being viewed', () => { + const expectedCategory = undefined; + const expectedAction = undefined; + const expectedLabel = 'suggest_commit_first_project_gitlab_ci_yml'; + const expectedProperty = 'owner'; + + document.body.dataset.page = 'projects:blob:new'; + + wrapper.vm.trackOnShow(); + + expect(trackingSpy).toHaveBeenCalledWith(expectedCategory, expectedAction, { + label: expectedLabel, + property: expectedProperty, + }); + }); + }); + describe('when the popover is mounted with the trackLabel of the Confirm button popover at the bottom of the page', () => { it('calls scrollToElement so that the Confirm button and popover will be in sight', () => { const scrollToElementSpy = jest.spyOn(utils, 'scrollToElement'); - const commitTrackLabel = 'suggest_commit_first_project_gitlab_ci_yml'; createWrapper(commitTrackLabel); diff --git a/spec/frontend/ide/components/preview/clientside_spec.js b/spec/frontend/ide/components/preview/clientside_spec.js index c7d5ea9c513..0cde6fb6060 100644 --- a/spec/frontend/ide/components/preview/clientside_spec.js +++ b/spec/frontend/ide/components/preview/clientside_spec.js @@ -16,6 +16,17 @@ const dummyPackageJson = () => ({ main: 'index.js', }), }); +const expectedSandpackOptions = () => ({ + files: {}, + entry: '/index.js', + showOpenInCodeSandbox: true, +}); +const expectedSandpackSettings = () => ({ + fileResolver: { + isFile: expect.any(Function), + readFile: expect.any(Function), + }, +}); describe('IDE clientside preview', () => { let wrapper; @@ -87,6 +98,46 @@ describe('IDE clientside preview', () => { it('creates sandpack manager', () => { expect(smooshpack.Manager).toHaveBeenCalledWith( '#ide-preview', + expectedSandpackOptions(), + expectedSandpackSettings(), + ); + }); + + it('pings usage', () => { + expect(storeClientsideActions.pingUsage).toHaveBeenCalledTimes(1); + }); + }); + + describe('with codesandboxBundlerUrl', () => { + const TEST_BUNDLER_URL = 'https://test.gitlab-static.test'; + + beforeEach(() => { + createComponent({ + getters: { packageJson: dummyPackageJson }, + state: { codesandboxBundlerUrl: TEST_BUNDLER_URL }, + }); + + return waitForCalls(); + }); + + it('creates sandpack manager with bundlerURL', () => { + expect(smooshpack.Manager).toHaveBeenCalledWith('#ide-preview', expectedSandpackOptions(), { + ...expectedSandpackSettings(), + bundlerURL: TEST_BUNDLER_URL, + }); + }); + }); + + describe('with codesandboxBundlerURL', () => { + beforeEach(() => { + createComponent({ getters: { packageJson: dummyPackageJson } }); + + return waitForCalls(); + }); + + it('creates sandpack manager', () => { + expect(smooshpack.Manager).toHaveBeenCalledWith( + '#ide-preview', { files: {}, entry: '/index.js', @@ -100,10 +151,6 @@ describe('IDE clientside preview', () => { }, ); }); - - it('pings usage', () => { - expect(storeClientsideActions.pingUsage).toHaveBeenCalledTimes(1); - }); }); describe('computed', () => { diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index dd6690b4d1e..42a7d567613 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -480,29 +480,39 @@ describe WikiPage do let(:untitled_page) { described_class.new(wiki) } let(:directory_page) do - create_page('parent/child', 'test content') - wiki.find_page('parent/child') + create_page('parent directory/child page', 'test content') + wiki.find_page('parent directory/child page') end where(:page, :title, :changed) do - :untitled_page | nil | false - :untitled_page | 'new title' | true - - :new_page | nil | true - :new_page | 'test page' | true - :new_page | 'new title' | true - - :existing_page | nil | false - :existing_page | 'test page' | false - :existing_page | '/test page' | false - :existing_page | 'new title' | true - - :directory_page | nil | false - :directory_page | 'parent/child' | false - :directory_page | 'child' | false - :directory_page | '/child' | true - :directory_page | 'parent/other' | true - :directory_page | 'other/child' | true + :untitled_page | nil | false + :untitled_page | 'new title' | true + + :new_page | nil | true + :new_page | 'test page' | true + :new_page | 'new title' | true + + :existing_page | nil | false + :existing_page | 'test page' | false + :existing_page | 'test-page' | false + :existing_page | '/test page' | false + :existing_page | '/test-page' | false + :existing_page | ' test page ' | true + :existing_page | 'new title' | true + :existing_page | 'new-title' | true + + :directory_page | nil | false + :directory_page | 'parent directory/child page' | false + :directory_page | 'parent-directory/child page' | false + :directory_page | 'parent-directory/child-page' | false + :directory_page | 'child page' | false + :directory_page | 'child-page' | false + :directory_page | '/child page' | true + :directory_page | 'parent directory/other' | true + :directory_page | 'parent-directory/other' | true + :directory_page | 'parent-directory / child-page' | true + :directory_page | 'other directory/child page' | true + :directory_page | 'other-directory/child page' | true end with_them do diff --git a/spec/support/shared_examples/csp.rb b/spec/support/shared_examples/csp.rb index 10c4158522f..c4a8c7df898 100644 --- a/spec/support/shared_examples/csp.rb +++ b/spec/support/shared_examples/csp.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.shared_examples 'setting CSP connect-src' do +RSpec.shared_examples 'setting CSP' do |rule_name| let_it_be(:default_csp_values) { "'self' https://some-cdn.test" } shared_context 'csp config' do |csp_rule| @@ -10,7 +10,7 @@ RSpec.shared_examples 'setting CSP connect-src' do end expect_next_instance_of(extended_controller_class) do |controller| - expect(controller).to receive(:current_content_security_policy).and_return(csp) + expect(controller).to receive(:current_content_security_policy).at_least(:once).and_return(csp) end end end @@ -23,55 +23,55 @@ RSpec.shared_examples 'setting CSP connect-src' do end end - describe 'when a CSP config exists for connect-src' do - include_context 'csp config', :connect_src + describe "when a CSP config exists for #{rule_name}" do + include_context 'csp config', rule_name.parameterize.underscore.to_sym context 'when feature is enabled' do - it 'appends to connect-src' do - is_expected.to eql("connect-src #{default_csp_values} #{whitelisted_url}") + it "appends to #{rule_name}" do + is_expected.to eql("#{rule_name} #{default_csp_values} #{whitelisted_url}") end end context 'when feature is disabled' do include_context 'disable feature' - it 'keeps original connect-src' do - is_expected.to eql("connect-src #{default_csp_values}") + it "keeps original #{rule_name}" do + is_expected.to eql("#{rule_name} #{default_csp_values}") end end end - describe 'when a CSP config exists for default-src but not connect-src' do + describe "when a CSP config exists for default-src but not #{rule_name}" do include_context 'csp config', :default_src context 'when feature is enabled' do - it 'uses default-src values in connect-src' do - is_expected.to eql("default-src #{default_csp_values}; connect-src #{default_csp_values} #{whitelisted_url}") + it "uses default-src values in #{rule_name}" do + is_expected.to eql("default-src #{default_csp_values}; #{rule_name} #{default_csp_values} #{whitelisted_url}") end end context 'when feature is disabled' do include_context 'disable feature' - it 'does not add connect-src' do + it "does not add #{rule_name}" do is_expected.to eql("default-src #{default_csp_values}") end end end - describe 'when a CSP config exists for font-src but not connect-src' do + describe "when a CSP config exists for font-src but not #{rule_name}" do include_context 'csp config', :font_src context 'when feature is enabled' do - it 'uses default-src values in connect-src' do - is_expected.to eql("font-src #{default_csp_values}; connect-src #{whitelisted_url}") + it "uses default-src values in #{rule_name}" do + is_expected.to eql("font-src #{default_csp_values}; #{rule_name} #{whitelisted_url}") end end context 'when feature is disabled' do include_context 'disable feature' - it 'does not add connect-src' do + it "does not add #{rule_name}" do is_expected.to eql("font-src #{default_csp_values}") end end diff --git a/yarn.lock b/yarn.lock index b93a588c992..a6348615309 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2779,10 +2779,10 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -codesandbox-api@^0.0.20: - version "0.0.20" - resolved "https://registry.yarnpkg.com/codesandbox-api/-/codesandbox-api-0.0.20.tgz#174bcd76c9f31521175c6bceabc37da6b1fbc30b" - integrity sha512-jhxZzAmjCKBZad8QWMeueiQVFE87igK6F2DBOEVFFJO6jgTXT8qjuzGYepr+B8bjgo/icN7bc/2xmEMBA63s2w== +codesandbox-api@0.0.23: + version "0.0.23" + resolved "https://registry.yarnpkg.com/codesandbox-api/-/codesandbox-api-0.0.23.tgz#bf650a21b5f3c2369e03f0c19d10b4e2ba255b4f" + integrity sha512-fFGBkIghDkQILh7iHYlpZU5sfWncCDb92FQSFE4rR3VBcTfUsD5VZgpQi+JjZQuwWIdfl4cOhcIFrUYwshUezA== codesandbox-import-util-types@^1.2.11: version "1.2.11" @@ -10334,12 +10334,12 @@ slugify@^1.3.1: resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.3.1.tgz#f572127e8535329fbc6c1edb74ab856b61ad7de2" integrity sha512-6BwyhjF5tG5P8s+0DPNyJmBSBePG6iMyhjvIW5zGdA3tFik9PtK+yNkZgTeiroCRGZYgkHftFA62tGVK1EI9Kw== -smooshpack@^0.0.54: - version "0.0.54" - resolved "https://registry.yarnpkg.com/smooshpack/-/smooshpack-0.0.54.tgz#9044358b85052d348b801f385678c8a0c76f2bb6" - integrity sha512-yIwEWb17hqoW5IaWyzO6O6nxY89I5UdRoGIZy5hihoqXP9OYcoMbBTxKwS57MeXSKdNA2rtk86rlCcOgAYIgrA== +smooshpack@^0.0.62: + version "0.0.62" + resolved "https://registry.yarnpkg.com/smooshpack/-/smooshpack-0.0.62.tgz#cb31b9f808f73de3146b050f84d044eb353b5503" + integrity sha512-lFuJV2f504/U78sifWy0V2FyoE/8mTgOXM4DL918ncNxAxbtu236XSCLAH3SQwXZWn0JdmRnWs/XU4+sIUVVmQ== dependencies: - codesandbox-api "^0.0.20" + codesandbox-api "0.0.23" codesandbox-import-utils "^1.2.3" lodash.isequal "^4.5.0" |