Documentation
¶
Overview ¶
Package quickaction finds and neutralizes the lines of a Markdown body that GitLab would run as quick actions.
GitLab executes /close, /merge, /assign and the rest from note bodies and issue and merge request descriptions sent through the REST API, with no parameter to turn it off. Every body this server sends passes Check.
Detection follows GitLab's extractor, read at gitlab-org/gitlab 829b21d284863743bde419836a053626309d02f6 (2026-09-25): lib/gitlab/quick_actions/extractor.rb, and the quick_action Banzai pipeline it calls (lib/banzai/pipeline/quick_action_pipeline.rb, lib/banzai/filter/quick_action_filter.rb, lib/banzai/filter/markdown_filter.rb and lib/banzai/filter/markdown_engines/glfm_markdown.rb). The extractor
- deletes every carriage return;
- renders the body with comrak and keeps the top-level paragraphs, which leaves out code blocks, block quotes (> and >>>), HTML blocks, lists, headings and tables;
- runs one regular expression over the source lines of each such paragraph, which skips inline code and a paragraph-level HTML pattern and matches a command as ^/name, then a space and arguments, or only white space to the end of the line.
This package ports step 2 from comrak (blocks.go) and step 3 from the extractor (scan.go). It deviates from GitLab on purpose in four places, each toward finding more lines, never fewer:
- Any name counts, not only the commands GitLab knows. GitLab's list grows every release and differs by edition and target, and a command that is harmless today is not tomorrow.
- Because GitLab consumes a known command's line and not an unknown one's, which backticks pair into inline code depends on the names. scanParagraph follows both possibilities and reports a line if either reaches it.
- GitLab first discards paragraphs whose rendered text has no line starting with a slash. That filter is not modeled.
- A description list term ("term" above ": details") is searched as a paragraph, since GitLab versions that render without the description list extension see a paragraph there.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type BlockedError ¶
type BlockedError struct {
Lines []Line
}
BlockedError refuses a body that GitLab would run quick actions from.
func (*BlockedError) Class ¶
func (e *BlockedError) Class() string
Class is the error class, "blocked".
func (*BlockedError) Error ¶
func (e *BlockedError) Error() string
Error renders the message under the blocked class of docs/architecture.md §6.5.
func (*BlockedError) Message ¶
func (e *BlockedError) Message() string
Message is the text without the class, for a caller that attaches the class itself. It names line numbers and commands, never other text of the body.
type Line ¶
type Line struct {
// Number is the 1-based line number in the body.
Number int
// Text is the line without its line ending.
Text string
// Command is the command name, lowercased, without the slash.
Command string
}
Line is one line of a body that GitLab would run as a quick action.
func Escape ¶
Escape puts a backslash before the slash of every line Find reports. CommonMark renders "\/" as "/", so the text reads the same, and the extractor, which wants the slash first on the line, no longer matches. It returns the new body and the lines it escaped, numbered as in body. Nothing else in the body changes, line endings included.
The backslash shows only where GitLab runs a command that CommonMark renders as code. GitLab pairs backticks one to one, so when a code span opens with two backticks and holds one, a /close line inside it runs, and the reader sees the backslash in the span. Escaping it is still right; the command must not run.