From 93e4efb74294f1c51529187bbfd3b73cd46a3544 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Sun, 28 Apr 2019 13:33:22 -0500 Subject: [PATCH] Default max import depth of 50 So even without a timeout, there is a limit on bonkers cases. Standard resolver defaults to INIFINTY since the standard does not define a depth limit. --- .rubocop.yml | 3 +++ lib/dhall/resolve.rb | 30 ++++++++++++++++++++---------- test/test_resolve.rb | 21 +++++++++++++++++++++ 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 6940451..d7b00fb 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -20,6 +20,9 @@ Metrics/BlockLength: Exclude: - dhall.gemspec +Metrics/ParameterLists: + CountKeywordArgs: false + Metrics/ModuleLength: Enabled: false diff --git a/lib/dhall/resolve.rb b/lib/dhall/resolve.rb index 250b55b..1a8888c 100644 --- a/lib/dhall/resolve.rb +++ b/lib/dhall/resolve.rb @@ -150,8 +150,9 @@ module Dhall end class ResolutionSet - def initialize(reader) + def initialize(reader, max_depth:) @reader = reader + @max_depth = max_depth @parents = [] @set = Hash.new { |h, k| h[k] = [] } end @@ -160,6 +161,9 @@ module Dhall p = Promise.new if @parents.include?(source.canonical) p.reject(ImportLoopException.new(source)) + elsif @parents.length + 1 > @max_depth + msg = "Max import depth of #{@max_depth} exceeded" + p.reject(ImportFailedException.new(msg)) else @set[source] << p end @@ -215,12 +219,15 @@ module Dhall path_reader: ReadPathSources, http_reader: StandardReadHttpSources, https_reader: http_reader, - environment_reader: ReadEnvironmentSources + environment_reader: ReadEnvironmentSources, + max_depth: Float::INFINITY ) - @path_resolutions = ResolutionSet.new(path_reader) - @http_resolutions = ResolutionSet.new(http_reader) - @https_resolutions = ResolutionSet.new(https_reader) - @env_resolutions = ResolutionSet.new(environment_reader) + @path_resolutions = ResolutionSet.new(path_reader, max_depth: max_depth) + @http_resolutions = ResolutionSet.new(http_reader, max_depth: max_depth) + @https_resolutions = ResolutionSet.new(https_reader, max_depth: max_depth) + @env_resolutions = ResolutionSet.new( + environment_reader, max_depth: max_depth + ) @deadline = Util::NoDeadline.new @cache = {} end @@ -307,7 +314,8 @@ module Dhall http_reader: ReadHttpSources, https_reader: http_reader, environment_reader: ReadEnvironmentSources, - ipfs_public_gateway: "cloudflare-ipfs.com" + ipfs_public_gateway: "cloudflare-ipfs.com", + max_depth: 50 ) super( path_reader: ReadPathAndIPFSSources.new( @@ -317,7 +325,7 @@ module Dhall public_gateway: ipfs_public_gateway ), http_reader: http_reader, https_reader: https_reader, - environment_reader: environment_reader + environment_reader: environment_reader, max_depth: max_depth ) end end @@ -325,13 +333,15 @@ module Dhall class LocalOnly < Standard def initialize( path_reader: ReadPathSources, - environment_reader: ReadEnvironmentSources + environment_reader: ReadEnvironmentSources, + max_depth: 50 ) super( path_reader: path_reader, environment_reader: environment_reader, http_reader: RejectSources, - https_reader: RejectSources + https_reader: RejectSources, + max_depth: max_depth ) end end diff --git a/test/test_resolve.rb b/test/test_resolve.rb index 527e2bd..bb52758 100644 --- a/test/test_resolve.rb +++ b/test/test_resolve.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "base64" +require "securerandom" require "webmock/minitest" require "minitest/autorun" @@ -132,6 +133,26 @@ class TestResolve < Minitest::Test ) end + def test_forever_no_loop + resolver = Dhall::Resolvers::LocalOnly.new( + path_reader: lambda do |sources| + sources.map do |_| + Promise.resolve(nil).then do + "./#{SecureRandom.hex}" + end + end + end + ) + + assert_raises Dhall::ImportFailedException do + Dhall.load( + "./start", + resolver: resolver, + timeout: Float::INFINITY + ).sync + end + end + def test_missing expr = Dhall::Import.new( Dhall::Import::IntegrityCheck.new, -- 2.38.5