diff --git a/lib/bashly/commands/doc.rb b/lib/bashly/commands/doc.rb index 2eaf7f47..50f263ee 100644 --- a/lib/bashly/commands/doc.rb +++ b/lib/bashly/commands/doc.rb @@ -75,7 +75,7 @@ def raw_data @raw_data ||= begin result = {} Dir["#{docs_dir}/*.yml"].each do |path| - result.merge! YAML.load_file(path) + result.merge! YAML.trusted_load_file(path) end result end diff --git a/lib/bashly/completions/completely.yaml b/lib/bashly/completions/completely.yaml index 6916ced4..b5aaad1f 100644 --- a/lib/bashly/completions/completely.yaml +++ b/lib/bashly/completions/completely.yaml @@ -1,156 +1,141 @@ -bashly: -- --help -- -h -- --version -- -v -- init -- preview -- validate -- generate -- add -- doc -- completions -- shell - -bashly init: &init -- --help -- -h -- --minimal -- -m - -bashly i: *init - -bashly preview: &preview -- --help -- -h - -bashly p: *preview - -bashly validate: &validate -- --help -- -h -- --verbose -- -v - -bashly v: *validate - -bashly generate: &generate -- --help -- -h -- --env -- --force -- --quiet -- --upgrade -- --watch -- --wrap -- -e -- -f -- -q -- -r -- -u -- -w - -bashly g: *generate - -bashly generate*--env: &env -- development -- production - -bashly generate*-e: *env -bashly g*--env: *env -bashly g*-e: *env - -bashly add: &add -- --help -- -h -- --force -- --list -- --source -- -f -- -l -- -s -- colors -- completions -- completions_script -- completions_yaml -- config -- help -- hooks -- lib -- settings -- strings -- test -- validations -- yaml - -bashly a: *add - -bashly doc: &doc -- --help -- -h -- --index -- -i -- arg -- arg.allowed -- arg.completions -- arg.default -- arg.help -- arg.name -- arg.repeatable -- arg.required -- arg.validate -- command -- command.alias -- command.args -- command.catch_all -- command.commands -- command.completions -- command.default -- command.dependencies -- command.environment_variables -- command.examples -- command.expose -- command.extensible -- command.filename -- command.filters -- command.flags -- command.footer -- command.function -- command.group -- command.help -- command.name -- command.private -- command.version -- environment_variable -- environment_variable.default -- environment_variable.help -- environment_variable.name -- environment_variable.private -- environment_variable.required -- flag -- flag.allowed -- flag.arg -- flag.completions -- flag.conflicts -- flag.default -- flag.help -- flag.long -- flag.private -- flag.repeatable -- flag.required -- flag.short -- flag.validate - -bashly completions: &completions -- --help -- -h -- --install -- -i - -bashly c: *completions - -bashly shell: &shell -- --help -- -h - -bashly s: *shell \ No newline at end of file +patterns: +- bashly [root options] +- bashly init|i [init options] +- bashly preview|p [preview options] +- bashly validate|v [validate options] +- bashly generate|build|g [generate options] +- bashly add|a [add options] +- bashly doc [doc options] +- bashly completions|c [completions options] +- bashly render [render options] +- bashly shell|s [shell options] + +options: + root: + - --help|-h + - --version|-v + init: + - --help|-h + - --minimal|-m + preview: + - --help|-h + validate: + - --help|-h + - --verbose|-v + generate: + - --help|-h + - --env|-e + - --force|-f + - --quiet|-q + - --upgrade|-u + - --watch|-w + - --wrap|-r + add: + - --help|-h + - --force|-f + - --list|-l + - --source|-s + doc: + - --help|-h + - --index|-i + completions: + - --help|-h + - --install|-i + - --uninstall|-u + render: + - --help|-h + - --watch|-w + - --show|-s + - --list|-l + - --about|-a + shell: + - --help|-h + +tokens: + env: + - development + - production + function: + source: + path: + library: + - colors + - completions + - completions_script + - completions_yaml + - config + - help + - hooks + - ini + - lib + - render_markdown + - render_markdown_github + - render_mandoc + - settings + - stacktrace + - strings + - validations + - yaml + doc: + - arg + - arg.allowed + - arg.completions + - arg.default + - arg.help + - arg.name + - arg.repeatable + - arg.required + - arg.validate + - command + - command.alias + - command.args + - command.catch_all + - command.commands + - command.completions + - command.default + - command.dependencies + - command.environment_variables + - command.examples + - command.expose + - command.extensible + - command.filename + - command.filters + - command.flags + - command.footer + - command.function + - command.group + - command.help + - command.help_header_override + - command.name + - command.private + - command.variables + - command.version + - environment_variable + - environment_variable.default + - environment_variable.help + - environment_variable.name + - environment_variable.private + - environment_variable.required + - environment_variable.validate + - flag + - flag.allowed + - flag.arg + - flag.completions + - flag.conflicts + - flag.default + - flag.help + - flag.long + - flag.needs + - flag.private + - flag.repeatable + - flag.required + - flag.short + - flag.unique + - flag.validate + - variable + - variable.name + - variable.value + render_source: + - :mandoc + - :markdown + - :markdown_github diff --git a/lib/bashly/completions/completely.yaml.gtx b/lib/bashly/completions/completely.yaml.gtx index 3890f668..998fdd76 100644 --- a/lib/bashly/completions/completely.yaml.gtx +++ b/lib/bashly/completions/completely.yaml.gtx @@ -1,85 +1,72 @@ -> bashly: +> patterns: +> - bashly [root options] +> - bashly init|i [init options] +> - bashly preview|p [preview options] +> - bashly validate|v [validate options] +> - bashly generate|build|g [generate options] +> - bashly add|a [add options] +> - bashly doc [doc options] +> - bashly completions|c [completions options] +> - bashly render [render options] +> - bashly shell|s [shell options] +> +> options: +> root: = help_flags -> - --version -> - -v -commands.each do |command| - = "- #{command}" -end -> -> bashly init: &init +> - --version|-v +> init: += help_flags +> - --minimal|-m +> preview: += help_flags +> validate: = help_flags -> - --minimal -> - -m -> -> bashly i: *init -> -> bashly preview: &preview +> - --verbose|-v +> generate: = help_flags -> -> bashly p: *preview -> -> bashly validate: &validate +> - --env|-e +> - --force|-f +> - --quiet|-q +> - --upgrade|-u +> - --watch|-w +> - --wrap|-r +> add: = help_flags -> - --verbose -> - -v -> -> bashly v: *validate -> -> bashly generate: &generate +> - --force|-f +> - --list|-l +> - --source|-s +> doc: = help_flags -> - --env -> - --force -> - --quiet -> - --upgrade -> - --watch -> - --wrap -> - -e -> - -f -> - -q -> - -r -> - -u -> - -w -> -> bashly g: *generate -> -> bashly generate*--env: &env -> - development -> - production -> -> bashly generate*-e: *env -> bashly g*--env: *env -> bashly g*-e: *env -> -> bashly add: &add +> - --index|-i +> completions: = help_flags -> - --force -> - --list -> - --source -> - -f -> - -l -> - -s +> - --install|-i +> - --uninstall|-u +> render: += help_flags +> - --watch|-w +> - --show|-s +> - --list|-l +> - --about|-a +> shell: += help_flags +> +> tokens: +> env: +> - development +> - production +> function: +> source: +> path: +> library: libs.each do |lib| - = "- #{lib}" + = " - #{lib}" end -> -> bashly a: *add -> -> bashly doc: &doc -= help_flags -> - --index -> - -i +> doc: docs.each do |doc| - = "- #{doc}" + = " - #{doc}" +end +> render_source: +render_sources.each do |source| + = " - #{source}" end -> -> bashly completions: &completions -= help_flags -> - --install -> - -i -> -> bashly c: *completions -> -> bashly shell: &shell -= help_flags -> -> bashly s: *shell diff --git a/lib/bashly/extensions/yaml.rb b/lib/bashly/extensions/yaml.rb index 7d21b804..58bcdbb1 100644 --- a/lib/bashly/extensions/yaml.rb +++ b/lib/bashly/extensions/yaml.rb @@ -2,14 +2,21 @@ require 'yaml' module YAML - # We trust our loaded YAMLs - # This patch is due to https://bugs.ruby-lang.org/issues/17866 - # StackOverflow: https://stackoverflow.com/questions/71191685/visit-psych-nodes-alias-unknown-alias-default-psychbadalias/71192990#71192990 class << self - alias load unsafe_load if YAML.respond_to? :unsafe_load + def trusted_load(content) + if YAML.respond_to? :unsafe_load + YAML.unsafe_load content + else + YAML.load content + end + end + + def trusted_load_file(path) + trusted_load File.read(path) + end def load_erb_file(path) - YAML.load ERB.new(File.read(path)).result + YAML.trusted_load ERB.new(File.read(path)).result end end end diff --git a/lib/bashly/library_source_config.rb b/lib/bashly/library_source_config.rb index 4f1d0918..6c621a2f 100644 --- a/lib/bashly/library_source_config.rb +++ b/lib/bashly/library_source_config.rb @@ -9,7 +9,7 @@ def initialize(path) end def data - @data ||= YAML.load_file path + @data ||= YAML.trusted_load_file path end def validated_data diff --git a/lib/bashly/message_strings.rb b/lib/bashly/message_strings.rb index 2929460e..19e888c4 100644 --- a/lib/bashly/message_strings.rb +++ b/lib/bashly/message_strings.rb @@ -13,7 +13,7 @@ def values private def values! - defaults = YAML.load_file asset('libraries/strings/strings.yml') + defaults = YAML.trusted_load_file asset('libraries/strings/strings.yml') defaults.merge project_strings end @@ -23,7 +23,7 @@ def project_strings def project_strings! if File.exist? project_strings_path - YAML.load_file project_strings_path + YAML.trusted_load_file project_strings_path else {} end diff --git a/spec/bashly/commands/generate_spec.rb b/spec/bashly/commands/generate_spec.rb index 811808b6..4975b669 100644 --- a/spec/bashly/commands/generate_spec.rb +++ b/spec/bashly/commands/generate_spec.rb @@ -228,7 +228,7 @@ end let(:bashly_config_path) { "#{source_dir}/bashly.yml" } - let(:bashly_config) { YAML.load_file bashly_config_path } + let(:bashly_config) { YAML.trusted_load_file bashly_config_path } let(:watch_double) { instance_double Watch, on_change: nil } it 'generates immediately and on change' do diff --git a/spec/bashly/commands/render_spec.rb b/spec/bashly/commands/render_spec.rb index 8e736654..d44ffc3a 100644 --- a/spec/bashly/commands/render_spec.rb +++ b/spec/bashly/commands/render_spec.rb @@ -67,7 +67,7 @@ describe 'SOURCE TARGET --watch' do let(:bashly_config_path) { "#{source_dir}/bashly.yml" } - let(:bashly_config) { YAML.load_file bashly_config_path } + let(:bashly_config) { YAML.trusted_load_file bashly_config_path } let(:watch_double) { instance_double Watch, on_change: nil } it 'generates immediately and on change' do diff --git a/spec/bashly/extensions/yaml_spec.rb b/spec/bashly/extensions/yaml_spec.rb index a44e029b..13287010 100644 --- a/spec/bashly/extensions/yaml_spec.rb +++ b/spec/bashly/extensions/yaml_spec.rb @@ -1,4 +1,30 @@ +require 'completely' + describe YAML do + describe '::trusted_load' do + it 'does not override YAML.load for other gems' do + completions = Completely::Completions.load 'spec/fixtures/completely/pattern.yml' + + expect(completions.config).to be_a Completely::PatternConfig + expect(completions).to be_valid + end + + it 'falls back to YAML.load when unsafe_load is not available' do + allow(described_class).to receive(:respond_to?).and_call_original + allow(described_class).to receive(:respond_to?).with(:unsafe_load).and_return false + + expect(described_class.trusted_load('name: bashly')).to eq 'name' => 'bashly' + end + end + + describe '::trusted_load_file' do + let(:path) { 'spec/fixtures/completely/pattern.yml' } + + it 'loads a trusted YAML file' do + expect(described_class.trusted_load_file(path)).to include 'patterns' + end + end + describe '::load_erb_file' do let(:path) { 'spec/fixtures/erb/simple.yml' } diff --git a/spec/fixtures/completely/pattern.yml b/spec/fixtures/completely/pattern.yml new file mode 100644 index 00000000..d1e51d6c --- /dev/null +++ b/spec/fixtures/completely/pattern.yml @@ -0,0 +1,14 @@ +patterns: +- bashly [root options] +- bashly generate [generate options] + +options: + root: + - --help|-h + generate: + - --env|-e + +tokens: + env: + - development + - production diff --git a/spec/spec_mixin.rb b/spec/spec_mixin.rb index 10f11bc2..72023a34 100644 --- a/spec/spec_mixin.rb +++ b/spec/spec_mixin.rb @@ -9,7 +9,7 @@ def reset_tmp_dir(create_src: false, init: false, example: nil) def load_fixture(filename) @loaded_fixtures ||= {} - @loaded_fixtures[filename] ||= YAML.load_file "spec/fixtures/#{filename}.yml" + @loaded_fixtures[filename] ||= YAML.trusted_load_file "spec/fixtures/#{filename}.yml" end def cp(source, target = 'spec/tmp/') diff --git a/support/runfile/completions.runfile b/support/runfile/completions.runfile index c905a527..42e3d7be 100644 --- a/support/runfile/completions.runfile +++ b/support/runfile/completions.runfile @@ -30,15 +30,17 @@ action do end helpers do - def help_flags = "- --help\n- -h" + def help_flags = " - --help|-h" def libs = Bashly::LibrarySource.new.config.keys def commands = Bashly::CLI.runner.commands.keys + + def render_sources = Bashly::RenderSource.internal.keys.map { |key| ":#{key}" } def docs Dir['lib/bashly/docs/*.yml'].each_with_object({}) do |path, hash| - hash.merge!(YAML.load_file(path)) + hash.merge!(YAML.trusted_load_file(path)) end.keys end @@ -53,4 +55,4 @@ helpers do nil end end -end \ No newline at end of file +end