diff --git a/.gitlab/ci/static-analysis.gitlab-ci.yml b/.gitlab/ci/static-analysis.gitlab-ci.yml index cbdbb01b2a8..b9d73d7770d 100644 --- a/.gitlab/ci/static-analysis.gitlab-ci.yml +++ b/.gitlab/ci/static-analysis.gitlab-ci.yml @@ -199,7 +199,7 @@ semgrep-appsec-custom-rules: --include app --include lib --include workhorse \ --exclude '*_test.go' --exclude spec --exclude qa > gl-sast-report.json || true variables: - CUSTOM_RULES_URL: https://gitlab.com/gitlab-com/gl-security/appsec/sast-custom-rules/-/raw/main/appsec-pings/rules.yml + CUSTOM_RULES_URL: https://gitlab.com/gitlab-com/gl-security/appsec/sast-custom-rules/-/raw/main/gitlab-sast-rules/rules.yml artifacts: paths: - gl-sast-report.json diff --git a/.gitlab/sast-ruleset.toml b/.gitlab/sast-ruleset.toml index 6bfb4618b73..e247d20da2c 100644 --- a/.gitlab/sast-ruleset.toml +++ b/.gitlab/sast-ruleset.toml @@ -7,4 +7,4 @@ type = "git" value = "https://gitlab.com/gitlab-com/gl-security/appsec/sast-custom-rules.git" ref = "refs/heads/main" - subdir = "appsec-pings" + subdir = "gitlab-sast-rules" diff --git a/.rubocop_todo/lint/symbol_conversion.yml b/.rubocop_todo/lint/symbol_conversion.yml index 4563b57c523..0fb6641d2a7 100644 --- a/.rubocop_todo/lint/symbol_conversion.yml +++ b/.rubocop_todo/lint/symbol_conversion.yml @@ -10,7 +10,6 @@ Lint/SymbolConversion: - 'config/puma.rb' - 'ee/app/components/billing/plan_component.rb' - 'ee/app/controllers/projects/security/scanned_resources_controller.rb' - - 'ee/app/models/product_analytics/jitsu_authentication.rb' - 'ee/app/serializers/integrations/zentao_serializers/issue_entity.rb' - 'ee/db/fixtures/development/35_merge_request_predictions.rb' - 'ee/lib/api/analytics/product_analytics.rb' diff --git a/.rubocop_todo/rspec/expect_change.yml b/.rubocop_todo/rspec/expect_change.yml index abcd80f9b58..02d1427c384 100644 --- a/.rubocop_todo/rspec/expect_change.yml +++ b/.rubocop_todo/rspec/expect_change.yml @@ -44,7 +44,6 @@ RSpec/ExpectChange: - 'ee/spec/models/group_wiki_spec.rb' - 'ee/spec/models/incident_management/issuable_escalation_status_spec.rb' - 'ee/spec/models/member_spec.rb' - - 'ee/spec/models/product_analytics/jitsu_authentication_spec.rb' - 'ee/spec/models/project_import_state_spec.rb' - 'ee/spec/models/push_rule_spec.rb' - 'ee/spec/models/security/orchestration_policy_configuration_spec.rb' diff --git a/.rubocop_todo/rspec/missing_feature_category.yml b/.rubocop_todo/rspec/missing_feature_category.yml index bc050a40a73..a7945a5356c 100644 --- a/.rubocop_todo/rspec/missing_feature_category.yml +++ b/.rubocop_todo/rspec/missing_feature_category.yml @@ -1185,7 +1185,6 @@ RSpec/MissingFeatureCategory: - 'ee/spec/models/path_lock_spec.rb' - 'ee/spec/models/plan_spec.rb' - 'ee/spec/models/preloaders/environments/protected_environment_preloader_spec.rb' - - 'ee/spec/models/product_analytics/jitsu_authentication_spec.rb' - 'ee/spec/models/productivity_analytics_spec.rb' - 'ee/spec/models/project_alias_spec.rb' - 'ee/spec/models/project_ci_cd_setting_spec.rb' diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 0291e87f7d1..131b990ed5e 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -20fe02e33885f459cddfb32717e02141d01ab12f +92e441aabf41c0793da9cb0ea1be58175f990f80 diff --git a/app/assets/javascripts/content_editor/extensions/paste_markdown.js b/app/assets/javascripts/content_editor/extensions/copy_paste.js similarity index 78% rename from app/assets/javascripts/content_editor/extensions/paste_markdown.js rename to app/assets/javascripts/content_editor/extensions/copy_paste.js index db13438de5e..45a89cc08cf 100644 --- a/app/assets/javascripts/content_editor/extensions/paste_markdown.js +++ b/app/assets/javascripts/content_editor/extensions/copy_paste.js @@ -2,6 +2,7 @@ import OrderedMap from 'orderedmap'; import { Extension } from '@tiptap/core'; import { Plugin, PluginKey } from '@tiptap/pm/state'; import { Schema, DOMParser as ProseMirrorDOMParser, DOMSerializer } from '@tiptap/pm/model'; +import { uniqueId } from 'lodash'; import { __ } from '~/locale'; import { VARIANT_DANGER } from '~/alert'; import createMarkdownDeserializer from '../services/gl_api_markdown_deserializer'; @@ -24,8 +25,23 @@ function parseHTML(schema, html) { return { document: ProseMirrorDOMParser.fromSchema(schema).parse(body) }; } +const findLoader = (editor, loaderId) => { + let position; + + editor.view.state.doc.descendants((descendant, pos) => { + if (descendant.type.name === 'loading' && descendant.attrs.id === loaderId) { + position = pos; + return false; + } + + return true; + }); + + return position; +}; + export default Extension.create({ - name: 'pasteMarkdown', + name: 'copyPaste', priority: EXTENSION_PRIORITY_HIGHEST, addOptions() { return { @@ -35,7 +51,7 @@ export default Extension.create({ }, addCommands() { return { - pasteContent: (content = '', processMarkdown = true) => async () => { + pasteContent: (content = '', processMarkdown = true) => () => { const { editor, options } = this; const { renderMarkdown, eventHub } = options; const deserializer = createMarkdownDeserializer({ render: renderMarkdown }); @@ -43,23 +59,37 @@ export default Extension.create({ const pasteSchemaSpec = { ...editor.schema.spec }; pasteSchemaSpec.marks = OrderedMap.from(pasteSchemaSpec.marks).remove('span'); pasteSchemaSpec.nodes = OrderedMap.from(pasteSchemaSpec.nodes).remove('div').remove('pre'); - const schema = new Schema(pasteSchemaSpec); + const pasteSchema = new Schema(pasteSchemaSpec); const promise = processMarkdown - ? deserializer.deserialize({ schema, markdown: content }) - : Promise.resolve(parseHTML(schema, content)); + ? deserializer.deserialize({ schema: pasteSchema, markdown: content }) + : Promise.resolve(parseHTML(pasteSchema, content)); + const loaderId = uniqueId('loading'); - promise - .then(({ document }) => { + Promise.resolve() + .then(() => { + editor.commands.insertContent({ type: 'loading', attrs: { id: loaderId } }); + return promise; + }) + .then(async ({ document }) => { if (!document) return; - const { firstChild } = document.content; + const pos = findLoader(editor, loaderId); + if (!pos) return; + + const { firstChild, childCount } = document.content; const toPaste = - document.content.childCount === 1 && firstChild.type.name === 'paragraph' + childCount === 1 && firstChild.type.name === 'paragraph' ? firstChild.content : document.content; - editor.commands.insertContent(toPaste.toJSON()); + editor + .chain() + .deleteRange({ from: pos, to: pos + 1 }) + .insertContentAt(pos, toPaste.toJSON(), { + updateSelection: false, + }) + .run(); }) .catch(() => { eventHub.$emit(ALERT_EVENT, { @@ -94,7 +124,7 @@ export default Extension.create({ return [ new Plugin({ - key: new PluginKey('pasteMarkdown'), + key: new PluginKey('copyPaste'), props: { handleDOMEvents: { copy: handleCutAndCopy, diff --git a/app/assets/javascripts/content_editor/extensions/loading.js b/app/assets/javascripts/content_editor/extensions/loading.js new file mode 100644 index 00000000000..0115fb10d5d --- /dev/null +++ b/app/assets/javascripts/content_editor/extensions/loading.js @@ -0,0 +1,23 @@ +import { Node } from '@tiptap/core'; + +export default Node.create({ + name: 'loading', + inline: true, + group: 'inline', + + addAttributes() { + return { + id: { + default: null, + }, + }; + }, + + renderHTML() { + return [ + 'span', + { class: 'gl-display-inline-flex gl-align-items-center' }, + ['span', { class: 'gl-dots-loader gl-mx-2' }, ['span']], + ]; + }, +}); diff --git a/app/assets/javascripts/content_editor/services/create_content_editor.js b/app/assets/javascripts/content_editor/services/create_content_editor.js index a0dc059b909..f7d189e443f 100644 --- a/app/assets/javascripts/content_editor/services/create_content_editor.js +++ b/app/assets/javascripts/content_editor/services/create_content_editor.js @@ -11,6 +11,7 @@ import Code from '../extensions/code'; import CodeBlockHighlight from '../extensions/code_block_highlight'; import CodeSuggestion from '../extensions/code_suggestion'; import ColorChip from '../extensions/color_chip'; +import CopyPaste from '../extensions/copy_paste'; import DescriptionItem from '../extensions/description_item'; import DescriptionList from '../extensions/description_list'; import Details from '../extensions/details'; @@ -40,10 +41,10 @@ import InlineDiff from '../extensions/inline_diff'; import Italic from '../extensions/italic'; import Link from '../extensions/link'; import ListItem from '../extensions/list_item'; +import Loading from '../extensions/loading'; import MathInline from '../extensions/math_inline'; import OrderedList from '../extensions/ordered_list'; import Paragraph from '../extensions/paragraph'; -import PasteMarkdown from '../extensions/paste_markdown'; import Reference from '../extensions/reference'; import ReferenceLabel from '../extensions/reference_label'; import ReferenceDefinition from '../extensions/reference_definition'; @@ -143,10 +144,11 @@ export const createContentEditor = ({ ExternalKeydownHandler.configure({ eventHub }), Link, ListItem, + Loading, MathInline, OrderedList, Paragraph, - PasteMarkdown.configure({ eventHub, renderMarkdown, serializer }), + CopyPaste.configure({ eventHub, renderMarkdown, serializer }), Reference.configure({ assetResolver }), ReferenceLabel, ReferenceDefinition, diff --git a/app/assets/javascripts/content_editor/services/markdown_serializer.js b/app/assets/javascripts/content_editor/services/markdown_serializer.js index bc738073e15..972b4acf523 100644 --- a/app/assets/javascripts/content_editor/services/markdown_serializer.js +++ b/app/assets/javascripts/content_editor/services/markdown_serializer.js @@ -32,6 +32,7 @@ import InlineDiff from '../extensions/inline_diff'; import Italic from '../extensions/italic'; import Link from '../extensions/link'; import ListItem from '../extensions/list_item'; +import Loading from '../extensions/loading'; import MathInline from '../extensions/math_inline'; import OrderedList from '../extensions/ordered_list'; import Paragraph from '../extensions/paragraph'; @@ -194,6 +195,7 @@ const defaultSerializerConfig = { inline: true, }), [ListItem.name]: preserveUnchanged(defaultMarkdownSerializer.nodes.list_item), + [Loading.name]: () => {}, [OrderedList.name]: preserveUnchanged(renderOrderedList), [Paragraph.name]: preserveUnchanged(defaultMarkdownSerializer.nodes.paragraph), [Reference.name]: renderReference, diff --git a/app/assets/javascripts/super_sidebar/components/brand_logo.vue b/app/assets/javascripts/super_sidebar/components/brand_logo.vue index c017fa8afa2..66381e4da4d 100644 --- a/app/assets/javascripts/super_sidebar/components/brand_logo.vue +++ b/app/assets/javascripts/super_sidebar/components/brand_logo.vue @@ -27,7 +27,7 @@ export default {