diff --git a/_config.yml b/_config.yml index 531e3c622aea..4f3a456326da 100644 --- a/_config.yml +++ b/_config.yml @@ -418,6 +418,8 @@ enable_video_embedding: false # enables video embedding for bibtex entries. If f # ----------------------------------------------------------------------------- # Add the url, version and integrity hash of the libraries you use in your site. +# The integrity hash is used to ensure that the library is not tampered with. +# Integrity hashes not provided by the libraries were generated using https://www.srihash.org/ third_party_libraries: download: false # if true, download the versions of the libraries specified below and use the downloaded files bootstrap-table: @@ -429,6 +431,8 @@ third_party_libraries: js: "https://cdn.jsdelivr.net/npm/bootstrap-table@{{version}}/dist/bootstrap-table.min.js" version: "1.22.4" chartjs: + integrity: + js: "sha256-0q+JdOlScWOHcunpUk21uab1jW7C1deBQARHtKMcaB4=" url: js: "https://cdn.jsdelivr.net/npm/chart.js@{{version}}/dist/chart.umd.min.js" version: "4.4.1" @@ -476,9 +480,14 @@ third_party_libraries: js: https://cdn.jsdelivr.net/npm/imagesloaded@{{version}}/imagesloaded.pkgd.min.js version: "5.0.0" img-comparison-slider: + integrity: + css: "sha256-3qTIuuUWIFnnU3LpQMjqiXc0p09rvd0dmj+WkpQXSR8=" + js: "sha256-EXHg3x1K4oIWdyohPeKX2ZS++Wxt/FRPH7Nl01nat1o=" + map: "sha256-3wfqS2WU5kGA/ePcgFzJXl5oSN1QsgZI4/edprTgX8w=" url: css: "https://cdn.jsdelivr.net/npm/img-comparison-slider@{{version}}/dist/styles.min.css" js: "https://cdn.jsdelivr.net/npm/img-comparison-slider@{{version}}/dist/index.min.js" + map: "https://cdn.jsdelivr.net/npm/img-comparison-slider@{{version}}/dist/index.js.map" version: "8.0.6" jquery: integrity: @@ -488,15 +497,20 @@ third_party_libraries: version: "3.6.0" leaflet: integrity: - css: "sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" - js: "sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" + css: "sha256-q9ba7o845pMPFU+zcAll8rv+gC+fSovKsOoNQ6cynuQ=" + js: "sha256-MgH13bFTTNqsnuEoqNPBLDaqxjGH+lCpqrukmXc8Ppg=" js_map: "sha256-YAoQ3FzREN4GmVENMir8vgHHypC0xfSK3CAxTHCqx1M=" + local: + images: "images/" url: - css: "https://cdn.jsdelivr.net/npm/leaflet@{{version}}/dist/leaflet.css" - js: "https://cdn.jsdelivr.net/npm/leaflet@{{version}}/dist/leaflet.js" + css: "https://cdn.jsdelivr.net/npm/leaflet@{{version}}/dist/leaflet.min.css" + images: "https://cdn.jsdelivr.net/npm/leaflet@{{version}}/dist/images/" + js: "https://cdn.jsdelivr.net/npm/leaflet@{{version}}/dist/leaflet.min.js" js_map: "https://cdn.jsdelivr.net/npm/leaflet@{{version}}/dist/leaflet.js.map" version: "1.9.4" mathjax: + integrity: + js: "sha256-rjmgmaB99riUNcdlrDtcAiwtLIojSxNyUFdl+Qh+rB4=" local: fonts: "output/chtml/fonts/woff-v2/" url: @@ -538,6 +552,9 @@ third_party_libraries: js: "https://cdnjs.cloudflare.com/polyfill/v{{version}}/polyfill.min.js?features=es6" version: "3" pseudocode: + integrity: + css: "sha256-VwMV//xgBPDyRFVSOshhRhzJRDyBmIACniLPpeXNUdc=" + js: "sha256-aVkDxqyzrB+ExUsOY9PdyelkDhn/DfrjWu08aVpqNlo=" url: css: "https://cdn.jsdelivr.net/npm/pseudocode@{{version}}/build/pseudocode.min.css" js: "https://cdn.jsdelivr.net/npm/pseudocode@{{version}}/build/pseudocode.min.js" @@ -546,9 +563,11 @@ third_party_libraries: integrity: css: "sha256-yUoNxsvX+Vo8Trj3lZ/Y5ZBf8HlBFsB6Xwm7rH75/9E=" js: "sha256-BPrwikijIybg9OQC5SYFFqhBjERYOn97tCureFgYH1E=" + map: "sha256-lbF5CsospW93otqvWOIbbhj80CjazrZXvamD7nC7TBI=" url: css: "https://cdn.jsdelivr.net/npm/swiper@{{version}}/swiper-bundle.min.css" js: "https://cdn.jsdelivr.net/npm/swiper@{{version}}/swiper-element-bundle.min.js" + map: "https://cdn.jsdelivr.net/npm/swiper@{{version}}/swiper-element-bundle.min.js.map" version: "11.0.5" swiper-map: integrity: diff --git a/_includes/head.liquid b/_includes/head.liquid index fc316baec30d..b4f32b06ecce 100644 --- a/_includes/head.liquid +++ b/_includes/head.liquid @@ -52,6 +52,7 @@ defer rel="stylesheet" href="{{ site.third_party_libraries.pseudocode.url.css }}" + integrity="{{ site.third_party_libraries.pseudocode.integrity.css }}" crossorigin="anonymous" > {% endif %} @@ -127,6 +128,8 @@ defer rel="stylesheet" href="{{ site.third_party_libraries.img-comparison-slider.url.css }}" + integrity="{{ site.third_party_libraries.img-comparison-slider.integrity.css }}" + crossorigin="anonymous" > {% endif %} diff --git a/_includes/scripts/chartjs.liquid b/_includes/scripts/chartjs.liquid index c64bdd3dcf68..bb21be2292ee 100644 --- a/_includes/scripts/chartjs.liquid +++ b/_includes/scripts/chartjs.liquid @@ -1,5 +1,10 @@ {% if page.chart and page.chart.chartjs %} - + + {% endif %} {% if page.images.slider %} + - {% endunless %} {% endif %} diff --git a/_includes/scripts/pseudocode.liquid b/_includes/scripts/pseudocode.liquid index 0db4ca897fde..5e9458b2559d 100644 --- a/_includes/scripts/pseudocode.liquid +++ b/_includes/scripts/pseudocode.liquid @@ -20,10 +20,14 @@ type="text/javascript" id="MathJax-script" src="{{ site.third_party_libraries.mathjax.url.js }}" + integrity="{{ site.third_party_libraries.mathjax.integrity.js }}" + crossorigin="anonymous" > + {% endif %} diff --git a/_plugins/download-3rd-party.rb b/_plugins/download-3rd-party.rb index 12dd9ad7467a..30bc1c6f9edb 100644 --- a/_plugins/download-3rd-party.rb +++ b/_plugins/download-3rd-party.rb @@ -6,6 +6,54 @@ require 'open-uri' require 'uri' + font_file_types = ['otf', 'ttf', 'woff', 'woff2'] + image_file_types = ['.gif', '.jpg', '.jpeg', '.png', '.webp'] + + def download_and_change_rule_set_url(rule_set, rule, dest, dirname, config, file_types) + # check if the rule has a url + if rule_set[rule].include?('url(') + # get the file url + url = rule_set[rule].split('url(').last.split(')').first + + # remove quotes from the url + if url.start_with?('"') || url.start_with?("'") + url = url[1..-2] + end + + file_name = url.split('/').last.split('?').first + + # verify if the file is of the correct type + if file_name.end_with?(*file_types) + # fix the url if it is not an absolute url + unless url.start_with?('https://') + url = URI.join(url, url).to_s + end + + # download the file + download_file(url, File.join(dest, file_name)) + + # change the url to the local file, considering baseurl + previous_rule = rule_set[rule] + if config['baseurl'] + # add rest of the src attribute if it exists + if rule_set[rule].split(' ').length > 1 + rule_set[rule] = "url(#{File.join(config['baseurl'], 'assets', 'libs', dirname, file_name)}) #{rule_set[rule].split(' ').last}" + else + rule_set[rule] = "url(#{File.join(config['baseurl'], 'assets', 'libs', dirname, file_name)})" + end + else + # add rest of the src attribute if it exists + if rule_set[rule].split(' ').length > 1 + rule_set[rule] = "url(#{File.join('/assets', 'libs', dirname, file_name)}) #{rule_set[rule].split(' ').last}" + else + rule_set[rule] = "url(#{File.join('/assets', 'libs', dirname, file_name)})" + end + end + puts "Changed #{previous_rule} to #{rule_set[rule]}" + end + end + end + def download_file(url, dest) # only try to download the file if url doesn't start with | for security reasons if url.start_with?('|') @@ -34,7 +82,7 @@ def download_file(url, dest) end end - def download_fonts(url, dest) + def download_fonts(url, dest, file_types) # only try to download the file if url doesn't start with | for security reasons if url.start_with?('|') return @@ -50,7 +98,7 @@ def download_fonts(url, dest) file_name = link['href'].split('/').last.split('?').first # verify if the file is a font file - if file_name.end_with?('.woff', '.woff2', '.ttf', '.otf') + if file_name.end_with?(*file_types) # download the file and change the url to the local file download_file(URI.join(url, link['href']).to_s, File.join(dest, file_name)) end @@ -58,7 +106,31 @@ def download_fonts(url, dest) end end - def download_fonts_from_css(config, url, dest) + def download_images(url, dest, file_types) + # only try to download the file if url doesn't start with | for security reasons + if url.start_with?('|') + return + end + + # only download images if the directory doesn't exist or is empty + unless File.directory?(dest) && !Dir.empty?(dest) + puts "Downloading images from #{url} to #{dest}" + # get available fonts from the url + doc = Nokogiri::HTML(URI.open(url, "User-Agent" => "Ruby/#{RUBY_VERSION}")) + doc.xpath('/html/body/div/div[3]/table/tbody/tr/td[1]/a').each do |link| + # get the file name from the url + file_name = link['href'].split('/').last.split('?').first + + # verify if the file is a font file + if file_name.end_with?(*file_types) + # download the file and change the url to the local file + download_file(URI.join(url, link['href']).to_s, File.join(dest, file_name)) + end + end + end + end + + def download_fonts_from_css(config, url, dest, lib_name, file_types) # only try to download the file if url doesn't start with | for security reasons if url.start_with?('|') return @@ -83,48 +155,11 @@ def download_fonts_from_css(config, url, dest) # get the font-face rules css.each_rule_set do |rule_set| # check if the rule set has a url - if rule_set['src'].include?('url(') - # get the font file url - font_url = rule_set['src'].split('url(').last.split(')').first - - # remove quotes from the url - if font_url.start_with?('"') || font_url.start_with?("'") - font_url = font_url[1..-2] - end - - font_file_name = font_url.split('/').last.split('?').first - - # verify if the file is a font file - if font_file_name.end_with?('.woff', '.woff2', '.ttf', '.otf') - # fix the font url if it is not an absolute url - unless font_url.start_with?('https://') - font_url = URI.join(url, font_url).to_s - end - - # download the file - download_file(font_url, File.join(dest, 'fonts', font_file_name)) - - # change the font url to the local file, considering baseurl - if config['baseurl'] - # add rest of the src attribute if it exists - if rule_set['src'].split(' ').length > 1 - rule_set['src'] = "url(#{File.join(config['baseurl'], 'assets', 'libs', 'fonts', font_file_name)}) #{rule_set['src'].split(' ').last}" - else - rule_set['src'] = "url(#{File.join(config['baseurl'], 'assets', 'libs', 'fonts', font_file_name)})" - end - else - # add rest of the src attribute if it exists - if rule_set['src'].split(' ').length > 1 - rule_set['src'] = "url(#{File.join('/assets', 'libs', 'fonts', font_file_name)}) #{rule_set['src'].split(' ').last}" - else - rule_set['src'] = "url(#{File.join('/assets', 'libs', 'fonts', font_file_name)})" - end - end - end - end + download_and_change_rule_set_url(rule_set, 'src', File.join(dest, 'fonts'), File.join(lib_name, 'fonts'), config, file_types) end # save the modified css file + puts "Saving modified css file to #{File.join(dest, file_name)}" File.write(File.join(dest, file_name), css.to_s) end @@ -164,13 +199,13 @@ def download_fonts_from_css(config, url, dest) # get the file name from the url file_name = url2.split('/').last.split('?').first # download the file and change the url to the local file - dest = File.join(site.source, 'assets', 'libs', file_name) + dest = File.join(site.source, 'assets', 'libs', key, file_name) download_file(url2, dest) # change the url to the local file, considering baseurl if site.config['baseurl'] - site.config['third_party_libraries'][key]['url'][type][type2] = File.join(site.config['baseurl'], 'assets', 'libs', file_name) + site.config['third_party_libraries'][key]['url'][type][type2] = File.join(site.config['baseurl'], 'assets', 'libs', key, file_name) else - site.config['third_party_libraries'][key]['url'][type][type2] = File.join('/assets', 'libs', file_name) + site.config['third_party_libraries'][key]['url'][type][type2] = File.join('/assets', 'libs', key, file_name) end end @@ -181,29 +216,33 @@ def download_fonts_from_css(config, url, dest) if file_name.end_with?('css') # if the file is a css file, download the css file, the fonts from it, and change information on the css file - file_name = download_fonts_from_css(site.config, url, File.join(site.source, 'assets', 'libs')) + file_name = download_fonts_from_css(site.config, url, File.join(site.source, 'assets', 'libs', key), key, font_file_types) # change the url to the local file, considering baseurl if site.config['baseurl'] - site.config['third_party_libraries'][key]['url'][type] = File.join(site.config['baseurl'], 'assets', 'libs', file_name) + site.config['third_party_libraries'][key]['url'][type] = File.join(site.config['baseurl'], 'assets', 'libs', key, file_name) else - site.config['third_party_libraries'][key]['url'][type] = File.join('/assets', 'libs', file_name) + site.config['third_party_libraries'][key]['url'][type] = File.join('/assets', 'libs', key, file_name) end else # download the font files and change the url to the local file - download_fonts(url, File.join(site.source, 'assets', 'libs', site.config['third_party_libraries'][key]['local']['fonts'])) + download_fonts(url, File.join(site.source, 'assets', 'libs', key, site.config['third_party_libraries'][key]['local'][type]), font_file_types) end + elsif type == 'images' + # download the font files and change the url to the local file + download_images(url, File.join(site.source, 'assets', 'libs', key, site.config['third_party_libraries'][key]['local'][type]), image_file_types) + else # get the file name from the url file_name = url.split('/').last.split('?').first # download the file and change the url to the local file - dest = File.join(site.source, 'assets', 'libs', file_name) + dest = File.join(site.source, 'assets', 'libs', key, file_name) download_file(url, dest) # change the url to the local file, considering baseurl if site.config['baseurl'] - site.config['third_party_libraries'][key]['url'][type] = File.join(site.config['baseurl'], 'assets', 'libs', file_name) + site.config['third_party_libraries'][key]['url'][type] = File.join(site.config['baseurl'], 'assets', 'libs', key, file_name) else - site.config['third_party_libraries'][key]['url'][type] = File.join('/assets', 'libs', file_name) + site.config['third_party_libraries'][key]['url'][type] = File.join('/assets', 'libs', key, file_name) end end end