Skip to content

Commit 88d7c47

Browse files
authored
Merge pull request #15075 from dependabot/brrygrdn/handle-unexpected-external-code
[Graph Job] Do not treat `Dependabot::UnexpectedExternalCode` as a hard failure
2 parents 033d0e7 + e4c219c commit 88d7c47

2 files changed

Lines changed: 126 additions & 1 deletion

File tree

updater/lib/dependabot/update_graph_processor.rb

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def run
7676
attr_reader :error_handler
7777

7878
sig { params(branch: String, directory: String).void }
79-
def process_directory(branch:, directory:)
79+
def process_directory(branch:, directory:) # rubocop:disable Metrics/MethodLength
8080
directory_source = create_source_for(directory)
8181
directory_dependency_files = dependency_files_for(directory)
8282

@@ -93,6 +93,36 @@ def process_directory(branch:, directory:)
9393

9494
Dependabot.logger.info("Dependency submission payload:\n#{JSON.pretty_generate(submission.payload)}")
9595
service.create_dependency_submission(dependency_submission: submission)
96+
rescue Dependabot::UnexpectedExternalCode
97+
# If this has been raised, then the directory is trying to use a private registry with an ecosystem
98+
# that requires the `insecure-external-code-execution: allow` flag.
99+
#
100+
# The default policy is denied, so this outcome represents a misconfiguration for the directory.
101+
# We should record this failure and allow other directories in the job to continue, as they may
102+
# not be misconfigured.
103+
Dependabot.logger.info(<<~LOG)
104+
Skipping directory #{directory} — Dependabot refused to execute external code
105+
106+
This directory is configured to use a private registry but does not allow insecure code execution via your Dependabot configuration.
107+
108+
Please set `insecure-external-code-execution: allow` in the config if you trust your dependencies'
109+
supply chain or remove the private registry from this directory.
110+
LOG
111+
service.record_update_job_error(
112+
error_type: "unexpected_external_code",
113+
error_details: { message: "Cannot process directory #{directory} without external code execution" }
114+
)
115+
116+
return unless Dependabot::Environment.github_actions?
117+
118+
service.create_dependency_submission(
119+
dependency_submission: empty_submission(
120+
branch,
121+
T.must(directory_source),
122+
GithubApi::DependencySubmission::SnapshotStatus::FAILED,
123+
"unexpected_external_code"
124+
)
125+
)
96126
rescue Dependabot::ApiError, Excon::Error::Socket, Excon::Error::Timeout, OpenSSL::SSL::SSLError
97127
# If the submission API is down, we should raise this as a specific error type for visibility.
98128
error_handler.handle_job_error(

updater/spec/dependabot/update_graph_processor_spec.rb

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,4 +791,99 @@ def fetch_subdependencies(_dependency)
791791
update_graph_processor.run
792792
end
793793
end
794+
795+
context "when external code execution is rejected" do
796+
let(:directories) { [dir1, dir2] }
797+
let(:dir1) { "/" }
798+
let(:dir2) { "/subproject" }
799+
let(:repo_contents_path) { build_tmp_repo("bundler/original", path: "") }
800+
801+
let(:dependency_files) do
802+
[
803+
Dependabot::DependencyFile.new(
804+
name: "Gemfile",
805+
content: fixture("bundler/original/Gemfile"),
806+
directory: dir1
807+
),
808+
Dependabot::DependencyFile.new(
809+
name: "Gemfile.lock",
810+
content: fixture("bundler/original/Gemfile.lock"),
811+
directory: dir1
812+
),
813+
Dependabot::DependencyFile.new(
814+
name: "Gemfile",
815+
content: fixture("bundler/original/Gemfile"),
816+
directory: dir2
817+
),
818+
Dependabot::DependencyFile.new(
819+
name: "Gemfile.lock",
820+
content: fixture("bundler/original/Gemfile.lock"),
821+
directory: dir2
822+
)
823+
]
824+
end
825+
826+
before do
827+
allow(Dependabot::FileParsers).to receive(:for_package_manager)
828+
.and_raise(Dependabot::UnexpectedExternalCode)
829+
end
830+
831+
context "when executing standalone" do
832+
before do
833+
allow(Dependabot::Environment).to receive(:github_actions?).and_return(false)
834+
end
835+
836+
it "records an error for each directory" do
837+
expect(service).to receive(:record_update_job_error).with(
838+
error_type: "unexpected_external_code",
839+
error_details: { message: "Cannot process directory / without external code execution" }
840+
)
841+
expect(service).to receive(:record_update_job_error).with(
842+
error_type: "unexpected_external_code",
843+
error_details: { message: "Cannot process directory /subproject without external code execution" }
844+
)
845+
846+
update_graph_processor.run
847+
end
848+
849+
it "does not send any dependency submissions" do
850+
expect(service).not_to receive(:create_dependency_submission)
851+
852+
update_graph_processor.run
853+
end
854+
855+
it "does not halt processing of remaining directories" do
856+
call_count = 0
857+
allow(service).to receive(:record_update_job_error) { call_count += 1 }
858+
859+
update_graph_processor.run
860+
861+
expect(call_count).to eq(2)
862+
end
863+
end
864+
865+
context "when executing in GitHub Actions" do
866+
before do
867+
allow(Dependabot::Environment).to receive(:github_actions?).and_return(true)
868+
end
869+
870+
it "sends a FAILED empty submission for each directory" do
871+
expect(service).to receive(:create_dependency_submission).twice do |args|
872+
payload = args[:dependency_submission].payload
873+
expect(payload[:metadata][:status]).to eql(
874+
GithubApi::DependencySubmission::SnapshotStatus::FAILED.serialize
875+
)
876+
expect(payload[:metadata][:reason]).to eql("unexpected_external_code")
877+
end
878+
879+
update_graph_processor.run
880+
end
881+
882+
it "records an error for each directory" do
883+
expect(service).to receive(:record_update_job_error).twice
884+
885+
update_graph_processor.run
886+
end
887+
end
888+
end
794889
end

0 commit comments

Comments
 (0)