jekyll-kw-sri 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +115 -0
- data/lib/jekyll-kw-sri.rb +81 -0
- data/lib/jekyll-kw-sri/configuration.rb +34 -0
- data/lib/jekyll-kw-sri/parser.rb +51 -0
- data/lib/version.rb +5 -0
- metadata +63 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2973377b85291043610eda6a787a2e0b90cd8a5bacc1e97307e12141d506922c
|
4
|
+
data.tar.gz: 0ce8ffe029793579111eb924cd2da6ae01542b597a8a8a1203275eefa2cb4302
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 562282c803da6ee51304bc436d2186b5f06e9ca66dca84453207570b9db17881be5d73ffbc5fc0bffaeaf3be77e2d56d2cd99f415a0295536e98640554850c0c
|
7
|
+
data.tar.gz: e6c3f3650a781d0391729bac08fc3ebe812c46fee4fe81b2a836d3810da207000b08a51bd3cdfec7eb5a54a45bd70d5d3494cef6c271cdda165fa9256b390ba5
|
data/README.md
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# jekyll-kw-sri
|
2
|
+
|
3
|
+
A plugin for jekyll to calculate [Subresource Integrity][Wikipedia SRI] (SRI) hashes for CSS (even SCSS and SASS) and JS files during build time.
|
4
|
+
|
5
|
+
> **Subresource Integrity** (SRI) is a security feature that enables browsers to verify that resources they fetch (for example, from a CDN) are delivered without unexpected manipulation. It works by allowing you to provide a cryptographic hash that a fetched resource must match.
|
6
|
+
|
7
|
+
from [Mozilla docs][Mozilla Subresource Integrity]
|
8
|
+
|
9
|
+
## Configuration
|
10
|
+
|
11
|
+
Add `kw-sri` section to `_config.yml` configure the plugin globally.
|
12
|
+
|
13
|
+
```yaml
|
14
|
+
kw-sri:
|
15
|
+
createTmpfile: false
|
16
|
+
hash_type: 'sha384'
|
17
|
+
write_source_mapping_url: true
|
18
|
+
```
|
19
|
+
|
20
|
+
Configuration values
|
21
|
+
|
22
|
+
| Key | Description | Values (**default**) |
|
23
|
+
|--------------------------|---------------------------------------------------|----------------------------|
|
24
|
+
| createTmpfile | Debug-Only, save the rendered sass or scss as css | **false**, true |
|
25
|
+
| hash_type | Which kind of integrity hash | sha256, **sha384**, sha512 |
|
26
|
+
| write_source_mapping_url | Add the map-file like to the css | false, **true** |
|
27
|
+
|
28
|
+
Add `sri: true` to **Front Matter** of `<page>` or `<post>` to activate the sri plugin.
|
29
|
+
|
30
|
+
## Build gem
|
31
|
+
|
32
|
+
## Publish gem
|
33
|
+
|
34
|
+
## Run tests
|
35
|
+
|
36
|
+
```sh
|
37
|
+
bundle exec rake test
|
38
|
+
```
|
39
|
+
|
40
|
+
### Appraisal - Gemfile Generator
|
41
|
+
|
42
|
+
[GitHub](https://github.com/thoughtbot/appraisal)
|
43
|
+
|
44
|
+
1. Create a `Appraisals` file
|
45
|
+
2. Generate `Gemfiles`
|
46
|
+
|
47
|
+
```sh
|
48
|
+
bundle exec appraisal generate
|
49
|
+
```
|
50
|
+
|
51
|
+
## Notes / Hints
|
52
|
+
|
53
|
+
### Site context is empty
|
54
|
+
|
55
|
+
Inside the `render(context)` function of a `Liquid::Tag` there is a context object. With that context you can get the `site` object, anyhow when you want to cretae your temporry **site** and **context** you need a workaround.
|
56
|
+
|
57
|
+
Normal way to get the site object from the render function of a custom tag
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
site = context.registers[:site]
|
61
|
+
```
|
62
|
+
|
63
|
+
Create a temporary site and context of a **jekyll** environment
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
site = Jekyll::Site.new(Jekyll::Configuration::DEFAULTS)
|
67
|
+
context = Liquid::Context.new({}, {}, { site: site })
|
68
|
+
```
|
69
|
+
|
70
|
+
### Base class for custom tag
|
71
|
+
Use `Jekyll::Tags::IncludeRelativeTag` instead of `Liquid::Tag` as base class of the custom jekyll tag `SriScssHashTag` will help to read the content of the scss or sass files.
|
72
|
+
|
73
|
+
### Find Scss converter
|
74
|
+
|
75
|
+
Sometimes, especially during testing, the site object is not perfectly setup. So the function `find_converter_instance` will throw an error.
|
76
|
+
|
77
|
+
**Default** implementation to find the converter.
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
converter = site.find_converter_instance(Jekyll::Converters::Scss)
|
81
|
+
```
|
82
|
+
|
83
|
+
**Workaround** implementation to find the converter.
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
converter = if defined? site.find_converter_instance
|
87
|
+
site.find_converter_instance(Jekyll::Converters::Scss)
|
88
|
+
else
|
89
|
+
site.getConverterImpl(::Jekyll::Converters::Scss)
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
## SRI Integrity
|
94
|
+
|
95
|
+
```shell
|
96
|
+
openssl dgst -sha256 -binary ./style.css | openssl base64 -A
|
97
|
+
```
|
98
|
+
|
99
|
+
## Setup Steps
|
100
|
+
|
101
|
+
```sh
|
102
|
+
bundle init
|
103
|
+
bundle add rake
|
104
|
+
bundle add simplecov
|
105
|
+
bundle add minitest
|
106
|
+
bundle add minitest-reporters
|
107
|
+
bundle add minitest-profile
|
108
|
+
bundle add rspec-mocks
|
109
|
+
bundle add rdiscount
|
110
|
+
bundle add redcarpet
|
111
|
+
bundle add shoulda
|
112
|
+
```
|
113
|
+
|
114
|
+
[Wikipedia SRI]: https://en.wikipedia.org/wiki/Subresource_Integrity
|
115
|
+
[Mozilla Subresource Integrity]: https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'jekyll-kw-sri/configuration'
|
4
|
+
require 'jekyll-kw-sri/parser'
|
5
|
+
|
6
|
+
require 'jekyll'
|
7
|
+
|
8
|
+
module Jekyll
|
9
|
+
module KargWare
|
10
|
+
# jekyll-kw-sri custom tag
|
11
|
+
class SriScssHashTag < Jekyll::Tags::IncludeRelativeTag
|
12
|
+
# class SriScssHashTag < Liquid::Tag
|
13
|
+
def initialize(tag_name, input, tokens)
|
14
|
+
super
|
15
|
+
|
16
|
+
raise 'Please enter a file path' if input.length <= 0
|
17
|
+
|
18
|
+
@scss_file = strip_or_self(input)
|
19
|
+
# File.exists? is file?
|
20
|
+
|
21
|
+
@tag_name = tag_name
|
22
|
+
end
|
23
|
+
|
24
|
+
# def syntax_example
|
25
|
+
# "{% #{@tag_name} css/main.scss %}"
|
26
|
+
# end
|
27
|
+
|
28
|
+
def render(context)
|
29
|
+
# return '' unless context.registers[:page]['sri']
|
30
|
+
|
31
|
+
# # Read the global configuration
|
32
|
+
# @sri_config = context.registers[:site].config['kw-sri'] || {}
|
33
|
+
|
34
|
+
cache_compiled_scss(@file, context, lambda {
|
35
|
+
if context.nil? || context.registers[:site].nil?
|
36
|
+
puts 'WARNING: There was no context, generate default site and context'
|
37
|
+
site = Jekyll::Site.new(Jekyll::Configuration::DEFAULTS)
|
38
|
+
context = Liquid::Context.new({}, {}, { site: site })
|
39
|
+
else
|
40
|
+
site = context.registers[:site]
|
41
|
+
end
|
42
|
+
|
43
|
+
converter = site.find_converter_instance(Jekyll::Converters::Scss)
|
44
|
+
|
45
|
+
result = super(context)
|
46
|
+
scss = result.gsub(/^---.*---/m, '')
|
47
|
+
data = converter.convert(scss)
|
48
|
+
|
49
|
+
Integrity::Parser.new(@sri_config).calc_integrity(@scss_file, data)
|
50
|
+
})
|
51
|
+
end
|
52
|
+
|
53
|
+
def cache_compiled_scss(path, _context, compute)
|
54
|
+
# @@cached_scss ||= {}
|
55
|
+
# if @@cached_scss.key?(path)
|
56
|
+
# @@cached_scss[path]
|
57
|
+
# else
|
58
|
+
# @@cached_scss[path] = compute.call
|
59
|
+
# end
|
60
|
+
|
61
|
+
@cached_scss ||= {}
|
62
|
+
if @cached_scss.key?(path)
|
63
|
+
@cached_scss[path]
|
64
|
+
else
|
65
|
+
@cached_scss[path] = compute.call
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# https://stackoverflow.com/a/1000975
|
70
|
+
def strip_or_self(str)
|
71
|
+
str.strip! || str
|
72
|
+
end
|
73
|
+
|
74
|
+
def tag_includes_dirs(context)
|
75
|
+
[context.registers[:site].source].freeze
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
Liquid::Template.register_tag('sri_scss_hash', Jekyll::KargWare::SriScssHashTag)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jekyll
|
4
|
+
module KargWare
|
5
|
+
module Integrity
|
6
|
+
# jekyll-kw-sri configuration class
|
7
|
+
class Configuration
|
8
|
+
attr_accessor :hash_type, :write_source_mapping_url, :create_tmpfile
|
9
|
+
|
10
|
+
DEFAULT_CONFIG = {
|
11
|
+
'hashType' => 'sha384',
|
12
|
+
'writeSourceMappingURL' => true,
|
13
|
+
'createTmpfile' => false
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def initialize(options)
|
17
|
+
options = generate_option_hash(options)
|
18
|
+
|
19
|
+
@hash_type = options['hashType']
|
20
|
+
@write_source_mapping_url = options['writeSourceMappingURL']
|
21
|
+
@create_tmpfile = options['createTmpfile']
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def generate_option_hash(options)
|
27
|
+
DEFAULT_CONFIG.merge(options)
|
28
|
+
rescue TypeError
|
29
|
+
DEFAULT_CONFIG
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
module Jekyll
|
6
|
+
module KargWare
|
7
|
+
module Integrity
|
8
|
+
# jekyll-kw-sri parser class
|
9
|
+
class Parser
|
10
|
+
attr_reader :configuration
|
11
|
+
|
12
|
+
def initialize(options = {})
|
13
|
+
@configuration = Configuration.new(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def calc_integrity(filename, data)
|
17
|
+
hash_type = @configuration.hash_type
|
18
|
+
|
19
|
+
data_modified = add_source_mapping_url(filename, data)
|
20
|
+
|
21
|
+
# Debuging, save rendered css file as tmp file
|
22
|
+
File.open(".#{filename}.tmp", 'w') { |file| file.write(data_modified) } if @configuration.create_tmpfile
|
23
|
+
|
24
|
+
case hash_type
|
25
|
+
when 'sha256'
|
26
|
+
"sha256-#{Digest::SHA256.base64digest data_modified}"
|
27
|
+
when 'sha384'
|
28
|
+
"sha384-#{Digest::SHA384.base64digest data_modified}"
|
29
|
+
when 'sha512'
|
30
|
+
"sha512-#{Digest::SHA512.base64digest data_modified}"
|
31
|
+
else
|
32
|
+
raise Jekyll::KargWare::Integrity::InvalidHashTypeException, "The type of the hash '#{hash_type}' is invalid!'"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_source_mapping_url(filename, data)
|
37
|
+
if @configuration.write_source_mapping_url
|
38
|
+
base = File.basename(filename)
|
39
|
+
base = base.sub! 'scss', 'css'
|
40
|
+
|
41
|
+
data + "\n/*# sourceMappingURL=#{base}.map */"
|
42
|
+
else
|
43
|
+
data
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class InvalidHashTypeException < Gem::Exception; end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jekyll-kw-sri
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nicolas Karg
|
8
|
+
- n13.org - Open-Source by KargWare
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2020-11-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: jekyll
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '4.0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '4.0'
|
28
|
+
description: Jekyll plugin which calculate the integrity hash of CSS (SCSS, SASS)
|
29
|
+
and JS.
|
30
|
+
email: rubygems.org@n13.org
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- README.md
|
36
|
+
- lib/jekyll-kw-sri.rb
|
37
|
+
- lib/jekyll-kw-sri/configuration.rb
|
38
|
+
- lib/jekyll-kw-sri/parser.rb
|
39
|
+
- lib/version.rb
|
40
|
+
homepage: https://github.com/n13org/jekyll-kw-sri
|
41
|
+
licenses:
|
42
|
+
- MIT
|
43
|
+
metadata: {}
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '2.7'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubygems_version: 3.1.2
|
60
|
+
signing_key:
|
61
|
+
specification_version: 4
|
62
|
+
summary: Jekyll CSS/JS integrity hash plugin
|
63
|
+
test_files: []
|