url_canonicalize 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,30 @@
3
3
  module URLCanonicalize
4
4
  # Make an HTTP request
5
5
  class Request
6
+ NETWORK_EXCEPTIONS = [
7
+ EOFError,
8
+ Errno::ECONNREFUSED,
9
+ Errno::ECONNRESET,
10
+ Errno::EHOSTUNREACH,
11
+ Errno::EINVAL,
12
+ Errno::ENETUNREACH,
13
+ Errno::ETIMEDOUT,
14
+ Net::OpenTimeout,
15
+ Net::ReadTimeout,
16
+ OpenSSL::SSL::SSLError,
17
+ SocketError,
18
+ Timeout::Error,
19
+ Zlib::BufError,
20
+ Zlib::DataError
21
+ ].freeze
22
+
23
+ # Unsuccessful responses to a HEAD request that trigger a retry with GET
24
+ HEAD_FALLBACK_RESPONSES = [
25
+ Net::HTTPForbidden,
26
+ Net::HTTPMethodNotAllowed,
27
+ Net::HTTPNotImplemented
28
+ ].freeze
29
+
6
30
  def fetch
7
31
  handle_response
8
32
  end
@@ -11,15 +35,13 @@ module URLCanonicalize
11
35
  @location ||= relative_to_absolute(response['location'])
12
36
  end
13
37
 
38
+ # Point this request at a new URI, discarding all state from the previous
39
+ # response so nothing leaks between hops
14
40
  def with_uri(uri)
15
41
  @uri = uri
16
42
 
17
43
  @url = nil
18
- @host = nil
19
- @request = nil
20
- @response = nil
21
- @location = nil
22
- @html = nil
44
+ self.http_method = @default_http_method
23
45
 
24
46
  self
25
47
  end
@@ -31,6 +53,7 @@ module URLCanonicalize
31
53
  def initialize(http, http_method = :head)
32
54
  @http = http
33
55
  @http_method = http_method
56
+ @default_http_method = http_method
34
57
  end
35
58
 
36
59
  def response
@@ -55,41 +78,51 @@ module URLCanonicalize
55
78
  when Net::HTTPRedirection
56
79
  handle_redirection
57
80
  else
58
- handle_failure
81
+ handle_unsuccessful
59
82
  end
60
83
  rescue *NETWORK_EXCEPTIONS => e
61
- handle_failure(e.class, e.message)
84
+ handle_failure(e.class, e.message, e)
62
85
  end
63
86
 
64
87
  def handle_success
65
- @canonical_url = $LAST_MATCH_INFO['url'] if (response['link'] || '') =~ /<(?<url>.+)>\s*;\s*rel="canonical"/i
88
+ @canonical_url = normalized(relative_to_absolute(URLCanonicalize::LinkHeader.canonical(response)))
66
89
 
67
90
  return enhanced_response if canonical_url || http_method == :get
68
91
 
92
+ fallback_to_get
93
+ end
94
+
95
+ # Some servers reject HEAD requests outright, so any HEAD request rejected
96
+ # with one of these statuses is retried as a GET before giving up
97
+ def handle_unsuccessful
98
+ return fallback_to_get if http_method == :head && HEAD_FALLBACK_RESPONSES.any? { |klass| response.is_a?(klass) }
99
+
100
+ handle_failure
101
+ end
102
+
103
+ def fallback_to_get
69
104
  self.http_method = :get
70
105
  fetch
71
106
  end
72
107
 
108
+ # Follow any redirection that carries a usable Location header, whether the
109
+ # redirect is temporary or permanent. A redirection without one cannot be
110
+ # followed, so it is reported as a failure.
73
111
  def handle_redirection
74
- case response
75
- when Net::HTTPFound, Net::HTTPMovedTemporarily, Net::HTTPTemporaryRedirect # Temporary redirection
76
- handle_success
77
- else # Permanent redirection
78
- if location
79
- URLCanonicalize::Response::Redirect.new(location)
80
- else
81
- URLCanonicalize::Response::Failure.new(::URI::InvalidURIError, response['location'])
82
- end
112
+ if location
113
+ URLCanonicalize::Response::Redirect.new(location)
114
+ else
115
+ URLCanonicalize::Response::Failure.new(::URI::InvalidURIError, response['location'])
83
116
  end
84
117
  end
85
118
 
86
- def handle_failure(klass = response.class, message = response.message)
87
- URLCanonicalize::Response::Failure.new(klass, message)
119
+ def handle_failure(klass = response.class, message = response.message, error = nil)
120
+ URLCanonicalize::Response::Failure.new(klass, message, error)
88
121
  end
89
122
 
90
123
  def enhanced_response
91
124
  if canonical_url
92
- puts " * canonical_url:\t#{canonical_url}" if ENV['DEBUG']
125
+ logger&.debug { "canonical_url: #{canonical_url}" }
93
126
  response_plus = URLCanonicalize::Response::Success.new(canonical_url, response, html)
94
127
  URLCanonicalize::Response::CanonicalFound.new(canonical_url, response_plus)
95
128
  else
@@ -98,19 +131,32 @@ module URLCanonicalize
98
131
  end
99
132
 
100
133
  def html
101
- @html ||= Nokogiri::HTML response.body
134
+ @html ||= Nokogiri::HTML(response.body) if URLCanonicalize::MediaType.html?(response)
102
135
  end
103
136
 
104
137
  def canonical_url
105
- @canonical_url ||= relative_to_absolute(canonical_url_raw)
138
+ @canonical_url ||= normalized(relative_to_absolute(canonical_url_element&.[]('href'), document_base_url))
106
139
  end
107
140
 
108
- def canonical_url_raw
109
- @canonical_url ||= canonical_url_element['href'] if canonical_url_element.is_a?(Nokogiri::XML::Element)
141
+ # Declared canonical URLs are returned to callers, so they get the same
142
+ # syntactic normalization as every requested URL
143
+ def normalized(url)
144
+ URLCanonicalize::URI.normalize(url) if url
110
145
  end
111
146
 
147
+ # The first HTML link element whose rel tokens include "canonical", however
148
+ # the tokens are cased, provided it has a usable href
112
149
  def canonical_url_element
113
- @canonical_url_element ||= html.xpath('//head/link[@rel="canonical"]').first
150
+ @canonical_url_element ||= html&.css('head link[rel]')&.find do |element|
151
+ element['rel'].split.any? { |token| token.casecmp?('canonical') } && !element['href'].to_s.strip.empty?
152
+ end
153
+ end
154
+
155
+ # HTML canonical links resolve against the document base URL, not the
156
+ # request URL, when the document declares one
157
+ def document_base_url
158
+ base_href = html&.at_css('head base[href]')&.[]('href')
159
+ relative_to_absolute(base_href) || url
114
160
  end
115
161
 
116
162
  def uri
@@ -121,10 +167,6 @@ module URLCanonicalize
121
167
  @url ||= uri.to_s
122
168
  end
123
169
 
124
- def host
125
- @host ||= uri.host
126
- end
127
-
128
170
  def request_for_method
129
171
  r = base_request
130
172
  headers.each { |header_key, header_value| r[header_key] = header_value }
@@ -132,8 +174,6 @@ module URLCanonicalize
132
174
  end
133
175
 
134
176
  def base_request
135
- check_http_method
136
-
137
177
  case http_method
138
178
  when :head
139
179
  Net::HTTP::Head.new uri
@@ -145,12 +185,7 @@ module URLCanonicalize
145
185
  end
146
186
 
147
187
  def headers
148
- @headers ||= {
149
- 'Accept-Language' => 'en-US,en;q=0.8',
150
- 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; WOW64) ' \
151
- 'AppleWebKit/537.36 (KHTML, like Gecko) ' \
152
- 'Chrome/51.0.2704.103 Safari/537.36'
153
- }
188
+ http.options[:headers]
154
189
  end
155
190
 
156
191
  def http_method=(value)
@@ -159,54 +194,28 @@ module URLCanonicalize
159
194
  @response = nil
160
195
  @location = nil
161
196
  @html = nil
197
+ @canonical_url = nil
198
+ @canonical_url_element = nil
162
199
  end
163
200
 
164
- # Some sites treat HEAD requests as suspicious activity and block the
165
- # requester after a few attempts. For these sites we'll use GET requests
166
- # only
167
- def check_http_method
168
- @http_method = :get if /(linkedin|crunchbase).com/ =~ host
169
- end
170
-
171
- def relative_to_absolute(partial_url)
172
- return unless partial_url
201
+ # Resolve absolute, protocol-relative, root-relative, path-relative and
202
+ # query-only references against the base URL per RFC 3986. Only http(s)
203
+ # results are usable
204
+ def relative_to_absolute(reference, base = url)
205
+ return unless reference
173
206
 
174
- partial_uri = ::URI.parse(partial_url)
175
-
176
- if partial_uri.host
177
- partial_url # It's already absolute
178
- else
179
- ::URI.join((uri || url), partial_url).to_s
180
- end
181
- rescue ::URI::InvalidURIError
207
+ absolute = ::URI.join(base, reference.strip)
208
+ absolute.to_s if absolute.is_a?(::URI::HTTP)
209
+ rescue ::URI::InvalidURIError, ArgumentError, Encoding::CompatibilityError
182
210
  nil
183
211
  end
184
212
 
185
213
  def log_response
186
- return unless ENV['DEBUG']
187
-
188
- puts "#{http_method.upcase} #{url} #{response.code} #{response.message}"
189
-
190
- return unless ENV['DEBUG'].casecmp('headers')
191
-
192
- response.each { |k, v| puts " #{k}:\t#{v}" }
214
+ logger&.debug { "#{http_method.upcase} #{url} #{response.code} #{response.message}" }
193
215
  end
194
216
 
195
- NETWORK_EXCEPTIONS = [
196
- EOFError,
197
- Errno::ECONNREFUSED,
198
- Errno::ECONNRESET,
199
- Errno::EHOSTUNREACH,
200
- Errno::EINVAL,
201
- Errno::ENETUNREACH,
202
- Errno::ETIMEDOUT,
203
- Net::OpenTimeout,
204
- Net::ReadTimeout,
205
- OpenSSL::SSL::SSLError,
206
- SocketError,
207
- Timeout::Error,
208
- Zlib::BufError,
209
- Zlib::DataError
210
- ].freeze
217
+ def logger
218
+ http.options[:logger]
219
+ end
211
220
  end
212
221
  end
@@ -13,7 +13,8 @@ module URLCanonicalize
13
13
  end
14
14
  end
15
15
 
16
- Redirect = Class.new(Generic)
16
+ # A redirect to another URL
17
+ class Redirect < Generic; end
17
18
 
18
19
  # Add HTML to a successful response
19
20
  class Success < Generic
@@ -44,15 +45,17 @@ module URLCanonicalize
44
45
  end
45
46
  end
46
47
 
47
- # It barfed
48
+ # It barfed. When the failure came from a rescued exception, `error` holds
49
+ # the original exception so callers can see the real cause
48
50
  class Failure
49
- attr_reader :failure_class, :message
51
+ attr_reader :failure_class, :message, :error
50
52
 
51
53
  private
52
54
 
53
- def initialize(failure_class, message)
55
+ def initialize(failure_class, message, error = nil)
54
56
  @failure_class = failure_class
55
57
  @message = message
58
+ @error = error
56
59
  end
57
60
  end
58
61
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module URLCanonicalize
4
+ # The outcome of one canonicalization: the canonical URL, the response
5
+ # that confirmed it, and the chain of hops that led there. Immutable
6
+ class Result
7
+ # One step in the request chain: the URL that was requested and how it
8
+ # was discovered (:initial, :redirect or :canonical_link)
9
+ Hop = Struct.new(:url, :via, keyword_init: true)
10
+
11
+ attr_reader :url, :response, :html, :chain, :source
12
+
13
+ def xml
14
+ Nokogiri::XML(response.body)
15
+ end
16
+
17
+ private
18
+
19
+ def initialize(url:, response:, html:, chain:, source:)
20
+ @url = url
21
+ @response = response
22
+ @html = html
23
+ @chain = chain.map(&:freeze).freeze
24
+ @source = source
25
+ freeze
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module URLCanonicalize
4
+ # Builds the connection for a destination, resolving the host through the
5
+ # fetch security policy. Injectable via the :transport option: anything
6
+ # responding to call(uri, options) and returning a Net::HTTP-compatible
7
+ # connection can replace it
8
+ class Transport
9
+ # Resolved lazily so the destination policy can be stubbed in tests
10
+ DEFAULT_RESOLVER = lambda do |uri, options|
11
+ URLCanonicalize::Destination.resolve(uri, options)
12
+ end
13
+
14
+ def initialize(resolver: DEFAULT_RESOLVER)
15
+ @resolver = resolver
16
+ freeze
17
+ end
18
+
19
+ def call(uri, options)
20
+ http = Net::HTTP.new(uri.host, uri.port, nil)
21
+
22
+ http.ipaddr = @resolver.call(uri, options)
23
+ configure_timeouts(http, options)
24
+ configure_tls(http, uri)
25
+
26
+ http
27
+ end
28
+
29
+ private
30
+
31
+ def configure_timeouts(http, options)
32
+ http.open_timeout = options[:open_timeout]
33
+ http.read_timeout = options[:read_timeout]
34
+ http.write_timeout = options[:write_timeout]
35
+ end
36
+
37
+ def configure_tls(http, uri)
38
+ if uri.scheme == 'https'
39
+ http.use_ssl = true # Can generate exception
40
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
41
+ http.verify_hostname = true
42
+ else
43
+ http.use_ssl = false
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,36 +1,49 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module URLCanonicalize
4
- # Manage the URL into a URI with local exception handling
4
+ # Parses and normalizes URLs with local exception handling
5
5
  class URI
6
6
  class << self
7
+ VALID_CLASSES = [::URI::HTTP, ::URI::HTTPS].freeze
8
+
9
+ # A scheme is recognized only as a leading scheme: token; a colon
10
+ # anywhere else no longer suppresses the default scheme
11
+ SCHEME = /\A[A-Za-z][A-Za-z0-9+.-]*:/
12
+
7
13
  def parse(url)
8
- uri = ::URI.parse decorate(url)
9
- uri if valid? uri
14
+ uri = ::URI.parse normalize(url)
15
+ validate! uri
16
+ uri
10
17
  rescue ::URI::InvalidURIError => e
11
- new_exception = URLCanonicalize::Exception::URI.new("#{e.class}: #{e.message}")
12
- new_exception.set_backtrace e.backtrace
13
- raise new_exception
18
+ raise URLCanonicalize::Exception::URI, "#{e.class}: #{e.message}" # the original error becomes the cause
19
+ end
20
+
21
+ # Syntactic normalization per RFC 3986, via Addressable: lowercase
22
+ # scheme and host, internationalized hosts to punycode, uppercase
23
+ # percent-encodings with unreserved characters decoded, dot segments
24
+ # resolved, default ports removed and the empty path replaced by "/".
25
+ # Fragments are client-side state, so no canonical URL carries one
26
+ def normalize(url)
27
+ addressable = Addressable::URI.parse(decorate(url.to_s.strip)).normalize
28
+ addressable.fragment = nil
29
+ addressable.to_s
30
+ rescue Addressable::URI::InvalidURIError, ArgumentError, Encoding::CompatibilityError => e
31
+ raise URLCanonicalize::Exception::URI, "#{e.class}: #{e.message}" # the original error becomes the cause
14
32
  end
15
33
 
16
34
  private
17
35
 
18
- def valid?(uri)
36
+ def validate!(uri)
19
37
  raise URLCanonicalize::Exception::URI, "#{uri} must be http or https" unless VALID_CLASSES.include?(uri.class)
20
38
  raise URLCanonicalize::Exception::URI, "Missing host name in #{uri}" unless uri.host
21
39
  raise URLCanonicalize::Exception::URI, "Empty host name in #{uri}" if uri.host.empty?
22
-
23
- true
24
40
  end
25
41
 
26
42
  def decorate(url)
27
- return url if url.include? COLON
43
+ return url if url.match?(SCHEME)
28
44
 
29
45
  "http://#{url}" # Add protocol if we just receive a host name
30
46
  end
31
-
32
- VALID_CLASSES = [::URI::HTTP, ::URI::HTTPS].freeze
33
- COLON = ':'
34
47
  end
35
48
  end
36
49
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module URLCanonicalize
4
- VERSION = '1.0.0'
4
+ VERSION = '2.0.0'
5
5
  end
@@ -4,30 +4,34 @@ require 'uri'
4
4
  require 'addressable/uri'
5
5
  require 'net/http'
6
6
  require 'nokogiri'
7
+ require 'timeout'
7
8
 
8
9
  autoload :OpenSSL, 'openssl'
9
10
 
10
11
  # Core methods
11
12
  module URLCanonicalize
13
+ autoload :BoundedBody, 'url_canonicalize/bounded_body'
14
+ autoload :Client, 'url_canonicalize/client'
15
+ autoload :Destination, 'url_canonicalize/destination'
12
16
  autoload :Exception, 'url_canonicalize/exception'
13
17
  autoload :HTTP, 'url_canonicalize/http'
18
+ autoload :LinkHeader, 'url_canonicalize/link_header'
19
+ autoload :MediaType, 'url_canonicalize/media_type'
20
+ autoload :Options, 'url_canonicalize/options'
14
21
  autoload :Request, 'url_canonicalize/request'
15
22
  autoload :Response, 'url_canonicalize/response'
23
+ autoload :Result, 'url_canonicalize/result'
24
+ autoload :Transport, 'url_canonicalize/transport'
16
25
  autoload :URI, 'url_canonicalize/uri'
17
26
  autoload :VERSION, 'url_canonicalize/version'
18
27
 
19
28
  class << self
20
- def canonicalize(url)
21
- fetch(url).url
29
+ def canonicalize(url, **)
30
+ Client.new(**).canonicalize(url)
22
31
  end
23
32
 
24
- def fetch(url)
25
- URLCanonicalize::HTTP.new(url).fetch
33
+ def fetch(url, **)
34
+ Client.new(**).fetch(url)
26
35
  end
27
36
  end
28
37
  end
29
-
30
- require 'monkey_patches/uri'
31
- require 'monkey_patches/string'
32
- require 'monkey_patches/addressable/uri'
33
- require 'English' # Needed for $LAST_MATCH_INFO
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: url_canonicalize
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominic Sayers
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-01-27 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: addressable
@@ -46,40 +45,39 @@ executables: []
46
45
  extensions: []
47
46
  extra_rdoc_files: []
48
47
  files:
49
- - ".codeclimate.yml"
50
- - ".github/workflows/codeql-analysis.yml"
51
- - ".gitignore"
52
- - ".hound.yml"
53
- - ".rspec"
54
- - ".rubocop.yml"
55
- - ".ruby-gemset"
56
- - ".travis.yml"
57
48
  - CHANGELOG.md
58
- - Gemfile
59
- - Gemfile.lock
60
- - Guardfile
61
49
  - LICENSE
62
50
  - README.md
63
- - Rakefile
64
- - circle.yml
65
- - lib/monkey_patches/addressable/uri.rb
66
- - lib/monkey_patches/string.rb
67
- - lib/monkey_patches/uri.rb
51
+ - SECURITY.md
68
52
  - lib/url_canonicalize.rb
53
+ - lib/url_canonicalize/bounded_body.rb
54
+ - lib/url_canonicalize/client.rb
55
+ - lib/url_canonicalize/core_ext.rb
56
+ - lib/url_canonicalize/core_ext/addressable.rb
57
+ - lib/url_canonicalize/core_ext/string.rb
58
+ - lib/url_canonicalize/core_ext/uri.rb
59
+ - lib/url_canonicalize/destination.rb
69
60
  - lib/url_canonicalize/exception.rb
70
61
  - lib/url_canonicalize/http.rb
62
+ - lib/url_canonicalize/link_header.rb
63
+ - lib/url_canonicalize/media_type.rb
64
+ - lib/url_canonicalize/options.rb
71
65
  - lib/url_canonicalize/request.rb
72
66
  - lib/url_canonicalize/response.rb
67
+ - lib/url_canonicalize/result.rb
68
+ - lib/url_canonicalize/transport.rb
73
69
  - lib/url_canonicalize/uri.rb
74
70
  - lib/url_canonicalize/version.rb
75
- - renovate.json
76
- - url_canonicalize.gemspec
77
71
  homepage: https://github.com/dominicsayers/url_canonicalize
78
72
  licenses:
79
73
  - MIT
80
74
  metadata:
75
+ bug_tracker_uri: https://github.com/dominicsayers/url_canonicalize/issues
76
+ changelog_uri: https://github.com/dominicsayers/url_canonicalize/blob/main/CHANGELOG.md
77
+ documentation_uri: https://www.rubydoc.info/gems/url_canonicalize
78
+ homepage_uri: https://github.com/dominicsayers/url_canonicalize
79
+ source_code_uri: https://github.com/dominicsayers/url_canonicalize
81
80
  rubygems_mfa_required: 'true'
82
- post_install_message:
83
81
  rdoc_options: []
84
82
  require_paths:
85
83
  - lib
@@ -87,15 +85,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
87
85
  requirements:
88
86
  - - ">="
89
87
  - !ruby/object:Gem::Version
90
- version: 3.1.0
88
+ version: 3.3.0
91
89
  required_rubygems_version: !ruby/object:Gem::Requirement
92
90
  requirements:
93
91
  - - ">="
94
92
  - !ruby/object:Gem::Version
95
93
  version: '0'
96
94
  requirements: []
97
- rubygems_version: 3.4.6
98
- signing_key:
95
+ rubygems_version: 4.0.16
99
96
  specification_version: 4
100
97
  summary: Finds the canonical version of a URL
101
98
  test_files: []
data/.codeclimate.yml DELETED
@@ -1,25 +0,0 @@
1
- ---
2
- engines:
3
- duplication:
4
- enabled: true
5
- config:
6
- languages:
7
- - ruby
8
- - javascript
9
- - python
10
- - php
11
- fixme:
12
- enabled: true
13
- rubocop:
14
- enabled: true
15
- ratings:
16
- paths:
17
- - "**.inc"
18
- - "**.js"
19
- - "**.jsx"
20
- - "**.module"
21
- - "**.php"
22
- - "**.py"
23
- - "**.rb"
24
- exclude_paths:
25
- - spec/
@@ -1,70 +0,0 @@
1
- # For most projects, this workflow file will not need changing; you simply need
2
- # to commit it to your repository.
3
- #
4
- # You may wish to alter this file to override the set of languages analyzed,
5
- # or to provide custom queries or build logic.
6
- #
7
- # ******** NOTE ********
8
- # We have attempted to detect the languages in your repository. Please check
9
- # the `language` matrix defined below to confirm you have the correct set of
10
- # supported CodeQL languages.
11
- #
12
- name: "CodeQL"
13
-
14
- on:
15
- push:
16
- branches: [ main ]
17
- pull_request:
18
- # The branches below must be a subset of the branches above
19
- branches: [ main ]
20
- schedule:
21
- - cron: '28 12 * * 5'
22
-
23
- jobs:
24
- analyze:
25
- name: Analyze
26
- runs-on: ubuntu-latest
27
- permissions:
28
- actions: read
29
- contents: read
30
- security-events: write
31
-
32
- strategy:
33
- fail-fast: false
34
- matrix:
35
- language: [ 'ruby' ]
36
- # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37
- # Learn more about CodeQL language support at https://git.io/codeql-language-support
38
-
39
- steps:
40
- - name: Checkout repository
41
- uses: actions/checkout@v4
42
-
43
- # Initializes the CodeQL tools for scanning.
44
- - name: Initialize CodeQL
45
- uses: github/codeql-action/init@v3
46
- with:
47
- languages: ${{ matrix.language }}
48
- # If you wish to specify custom queries, you can do so here or in a config file.
49
- # By default, queries listed here will override any specified in a config file.
50
- # Prefix the list here with "+" to use these queries and those in the config file.
51
- # queries: ./path/to/local/query, your-org/your-repo/queries@main
52
-
53
- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54
- # If this step fails, then you should remove it and run the build manually (see below)
55
- - name: Autobuild
56
- uses: github/codeql-action/autobuild@v3
57
-
58
- # ℹ️ Command-line programs to run using the OS shell.
59
- # 📚 https://git.io/JvXDl
60
-
61
- # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62
- # and modify them (or add more) to build your code if your project
63
- # uses a compiled language
64
-
65
- #- run: |
66
- # make bootstrap
67
- # make release
68
-
69
- - name: Perform CodeQL Analysis
70
- uses: github/codeql-action/analyze@v3