# frozen_string_literal: true require 'spec_helper' require 'nokogiri' module Gitlab RSpec.describe Asciidoc do include FakeBlobHelpers before do allow_any_instance_of(ApplicationSetting).to receive(:current).and_return(::ApplicationSetting.create_from_defaults) end context "without project" do let(:input) { 'ascii' } let(:context) { {} } let(:html) { 'H2O' } it "converts the input using Asciidoctor and default options" do expected_asciidoc_opts = { safe: :secure, backend: :gitlab_html5, attributes: described_class::DEFAULT_ADOC_ATTRS, extensions: be_a(Proc) } expect(Asciidoctor).to receive(:convert) .with(input, expected_asciidoc_opts).and_return(html) expect(render(input, context)).to eq(html) end context "with asciidoc_opts" do it "merges the options with default ones" do expected_asciidoc_opts = { safe: :secure, backend: :gitlab_html5, attributes: described_class::DEFAULT_ADOC_ATTRS, extensions: be_a(Proc) } expect(Asciidoctor).to receive(:convert) .with(input, expected_asciidoc_opts).and_return(html) render(input, context) end end context "with requested path" do input = <<~ADOC Document name: {docname}. ADOC it "ignores {docname} when not available" do expect(render(input, {})).to include(input.strip) end [ ['/', '', 'root'], ['README', 'README', 'just a filename'], ['doc/api/', '', 'a directory'], ['doc/api/README.adoc', 'README', 'a complete path'] ].each do |path, basename, desc| it "sets {docname} for #{desc}" do expect(render(input, { requested_path: path })).to include(": #{basename}.") end end end context "XSS" do items = { 'link with extra attribute' => { input: 'link:mylink"onmouseover="alert(1)[Click Here]', output: "
\">
\nAn admonition paragraph, like this note, grabs the reader’s attention. |
This paragraph has a footnote.[1]
checked
not checked
Werewolves are allergic to cassia cinnamon.
Did the werewolves read the small print?
Where did all the cores run off to?
We need ten make that twenty VMs.
Once upon an infinite loop.
console.log('hello world')
#include <stdio.h>
for (int i = 0; i < 5; i++) {
std::cout<<"*"<<std::endl;
}
eta_x gamma
')
expect(render(input, context)).to include('2+2
is 4
Include this:
') end end context 'with path to non-existing file' do let(:include_path) { 'not-exists.adoc' } it 'renders Unresolved directive placeholder' do is_expected.to include("[ERROR: include::#{include_path}[] - unresolved directive]") end end shared_examples :invalid_include do let(:include_path) { 'dk.png' } before do allow(project.repository).to receive(:blob_at).and_return(blob) end it 'does not read the blob' do expect(blob).not_to receive(:data) end it 'renders Unresolved directive placeholder' do is_expected.to include("[ERROR: include::#{include_path}[] - unresolved directive]") end end context 'with path to a binary file' do let(:blob) { fake_blob(path: 'dk.png', binary: true) } include_examples :invalid_include end context 'with path to file in external storage' do let(:blob) { fake_blob(path: 'dk.png', lfs: true) } before do allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) project.update_attribute(:lfs_enabled, true) end include_examples :invalid_include end context 'with path to a textual file' do let(:include_path) { 'sample.adoc' } before do create_file(file_path, "Content from #{include_path}") end shared_examples :valid_include do [ ['/doc/sample.adoc', 'doc/sample.adoc', 'absolute path'], ['sample.adoc', 'doc/api/sample.adoc', 'relative path'], ['./sample.adoc', 'doc/api/sample.adoc', 'relative path with leading ./'], ['../sample.adoc', 'doc/sample.adoc', 'relative path to a file up one directory'], ['../../sample.adoc', 'sample.adoc', 'relative path for a file up multiple directories'] ].each do |include_path_, file_path_, desc| context "the file is specified by #{desc}" do let(:include_path) { include_path_ } let(:file_path) { file_path_ } it 'includes content of the file' do is_expected.to include('Include this:
') is_expected.to include("Content from #{include_path}
") end end end end context 'when requested path is a file in the repo' do let(:requested_path) { 'doc/api/README.adoc' } include_examples :valid_include context 'without a commit (only ref)' do let(:commit) { nil } include_examples :valid_include end end context 'when requested path is a directory in the repo' do let(:requested_path) { 'doc/api/' } include_examples :valid_include context 'without a commit (only ref)' do let(:commit) { nil } include_examples :valid_include end end end context 'when repository is passed into the context' do let(:wiki_repo) { project.wiki.repository } let(:include_path) { 'wiki_file.adoc' } before do project.create_wiki context.merge!(repository: wiki_repo) end context 'when the file exists' do before do create_file(include_path, 'Content from wiki', repository: wiki_repo) end it { is_expected.to include('Content from wiki
') } end context 'when the file does not exist' do it { is_expected.to include("[ERROR: include::#{include_path}[] - unresolved directive]")} end end context 'recursive includes with relative paths' do let(:input) do <<~ADOC Source: requested file include::doc/README.adoc[] include::license.adoc[] ADOC end before do create_file 'doc/README.adoc', <<~ADOC Source: doc/README.adoc include::../license.adoc[] include::api/hello.adoc[] ADOC create_file 'license.adoc', <<~ADOC Source: license.adoc ADOC create_file 'doc/api/hello.adoc', <<~ADOC Source: doc/api/hello.adoc include::./common.adoc[] ADOC create_file 'doc/api/common.adoc', <<~ADOC Source: doc/api/common.adoc ADOC end it 'includes content of the included files recursively' do expect(output.gsub(/<[^>]+>/, '').gsub(/\n\s*/, "\n").strip).to eq <<~ADOC.strip Source: requested file Source: doc/README.adoc Source: license.adoc Source: doc/api/hello.adoc Source: doc/api/common.adoc Source: license.adoc ADOC end end def create_file(path, content, repository: project.repository) repository.create_file(project.creator, path, content, message: "Add #{path}", branch_name: 'asciidoc') end end end def render(*args) described_class.render(*args) end end end