graphql 2.6.8 → 2.6.9
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.
- checksums.yaml +4 -4
- data/lib/graphql/language/cache.rb +84 -14
- data/lib/graphql/railtie.rb +2 -1
- data/lib/graphql/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6a359d53729477046ffd6b51ef9345099defcdc95521579711fffe5ab79c0b5e
|
|
4
|
+
data.tar.gz: 4c88aecd90fb26e02198f21f4bdf7a73f577ec37ac9ea9eb6c840c8a2957fc7a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d40bb56d8b5e77ca6f126f1528e2a074bbf679996dfb8ea5a12d646d028d547848dc36dea3a5fb73273fb7252135a5c7e904ec7d0a38790fe05164c5e81701b5
|
|
7
|
+
data.tar.gz: e486b9492693cdc69375f8d3bc662e075a71ff45642f62cb2fd741959b2a2d1f9105fa14718b992ef2d3b3a809fd0f0d94ec66abf509137b3a7af05877527c23
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
require 'graphql/version'
|
|
4
4
|
require 'digest/sha2'
|
|
5
|
+
require 'openssl'
|
|
6
|
+
require 'securerandom'
|
|
7
|
+
require 'tempfile'
|
|
5
8
|
|
|
6
9
|
module GraphQL
|
|
7
10
|
module Language
|
|
@@ -9,7 +12,11 @@ module GraphQL
|
|
|
9
12
|
#
|
|
10
13
|
# With Rails, parser caching may enabled by setting `config.graphql.parser_cache = true` in your Rails application.
|
|
11
14
|
#
|
|
12
|
-
# The cache may be manually built by assigning `GraphQL::Language::Parser.cache = GraphQL::Language::Cache.new("some_dir")`.
|
|
15
|
+
# The cache may be manually built by assigning `GraphQL::Language::Parser.cache = GraphQL::Language::Cache.new(Pathname.new("some_dir"), secret: ENV.fetch("GRAPHQL_CACHE_SECRET"))`.
|
|
16
|
+
# The `secret` should be a stable value stored outside of the cache directory.
|
|
17
|
+
# When it isn't provided, a process-local secret is generated and cache entries
|
|
18
|
+
# are rebuilt after the process restarts. Pass `secret: nil` to disable cache
|
|
19
|
+
# signing. This should only be used when the cache directory is trusted.
|
|
13
20
|
# This will create a directory (`tmp/cache/graphql` by default) that stores a cache of parsed files.
|
|
14
21
|
#
|
|
15
22
|
# Much like [bootsnap](https://github.com/Shopify/bootsnap), the parser cache needs to be cleaned up manually.
|
|
@@ -18,32 +25,95 @@ module GraphQL
|
|
|
18
25
|
#
|
|
19
26
|
# @see GraphQL::Railtie for simple Rails integration
|
|
20
27
|
class Cache
|
|
21
|
-
|
|
28
|
+
# @param path [Pathname] The directory where cache entries are stored.
|
|
29
|
+
# @param secret [String, nil] A stable secret for verifying cache entries. When omitted,
|
|
30
|
+
# a process-local secret is generated. Pass `nil` to disable cache signing.
|
|
31
|
+
def initialize(path, secret: SecureRandom.random_bytes(32))
|
|
22
32
|
@path = path
|
|
33
|
+
@secret = secret
|
|
23
34
|
end
|
|
24
35
|
|
|
25
36
|
DIGEST = Digest::SHA256.new << GraphQL::VERSION
|
|
37
|
+
HMAC_SIZE = OpenSSL::Digest::SHA256.new.digest_length
|
|
38
|
+
InvalidCache = Class.new(StandardError)
|
|
39
|
+
private_constant :InvalidCache
|
|
26
40
|
|
|
27
41
|
def fetch(filename)
|
|
28
|
-
|
|
42
|
+
cache_key = cache_key_for(filename)
|
|
43
|
+
return yield unless cache_key
|
|
44
|
+
|
|
45
|
+
cache_path = @path.join(cache_key)
|
|
46
|
+
|
|
47
|
+
begin
|
|
48
|
+
return load_cache(cache_path, cache_key) if cache_path.file?
|
|
49
|
+
rescue InvalidCache, SystemCallError
|
|
50
|
+
# Rebuild caches created by older versions or with an invalid signature.
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
payload = yield
|
|
29
54
|
begin
|
|
30
|
-
|
|
55
|
+
write_cache(cache_path, cache_key, payload)
|
|
31
56
|
rescue SystemCallError
|
|
32
|
-
return
|
|
57
|
+
# Parser caching is best-effort; return the parsed payload if the cache cannot be written.
|
|
33
58
|
end
|
|
34
|
-
|
|
59
|
+
payload
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def cache_key_for(filename)
|
|
65
|
+
content_digest = Digest::SHA256.file(filename).hexdigest
|
|
66
|
+
(DIGEST.dup << filename << content_digest).to_s
|
|
67
|
+
rescue SystemCallError
|
|
68
|
+
nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def load_cache(cache_path, cache_key)
|
|
72
|
+
cache_data = cache_path.binread
|
|
73
|
+
return Marshal.load(cache_data) unless @secret
|
|
74
|
+
|
|
75
|
+
signature = cache_data.byteslice(0, HMAC_SIZE)
|
|
76
|
+
payload = cache_data.byteslice(HMAC_SIZE..-1)
|
|
77
|
+
raise InvalidCache unless signature && payload
|
|
35
78
|
|
|
36
|
-
|
|
37
|
-
|
|
79
|
+
expected_signature = signature_for(cache_key, payload)
|
|
80
|
+
unless secure_compare(signature, expected_signature)
|
|
81
|
+
raise InvalidCache
|
|
82
|
+
end
|
|
83
|
+
Marshal.load(payload)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def write_cache(cache_path, cache_key, payload)
|
|
87
|
+
@path.mkpath
|
|
88
|
+
serialized_payload = Marshal.dump(payload)
|
|
89
|
+
cache_data = if @secret
|
|
90
|
+
signature_for(cache_key, serialized_payload) + serialized_payload
|
|
38
91
|
else
|
|
39
|
-
|
|
40
|
-
|
|
92
|
+
serialized_payload
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
Tempfile.create(['graphql-cache-', '.tmp'], @path.to_s) do |tempfile|
|
|
96
|
+
tempfile.binmode
|
|
97
|
+
tempfile.write(cache_data)
|
|
98
|
+
tempfile.flush
|
|
99
|
+
tempfile.fsync
|
|
100
|
+
tempfile.close
|
|
101
|
+
File.rename(tempfile.path, cache_path.to_s)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def signature_for(cache_key, payload)
|
|
106
|
+
OpenSSL::HMAC.digest('SHA256', @secret, cache_key + payload)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def secure_compare(left, right)
|
|
110
|
+
return false unless left.bytesize == right.bytesize
|
|
41
111
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
payload
|
|
112
|
+
result = 0
|
|
113
|
+
left.bytes.each_with_index do |byte, index|
|
|
114
|
+
result |= byte ^ right.getbyte(index)
|
|
46
115
|
end
|
|
116
|
+
result.zero?
|
|
47
117
|
end
|
|
48
118
|
end
|
|
49
119
|
end
|
data/lib/graphql/railtie.rb
CHANGED
|
@@ -15,7 +15,8 @@ module GraphQL
|
|
|
15
15
|
initializer("graphql.cache") do |app|
|
|
16
16
|
if config.graphql.parser_cache
|
|
17
17
|
Language::Parser.cache ||= Language::Cache.new(
|
|
18
|
-
app.root.join("tmp/cache/graphql")
|
|
18
|
+
app.root.join("tmp/cache/graphql"),
|
|
19
|
+
secret: app.secret_key_base
|
|
19
20
|
)
|
|
20
21
|
end
|
|
21
22
|
end
|
data/lib/graphql/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: graphql
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.6.
|
|
4
|
+
version: 2.6.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Robert Mosolgo
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-08-
|
|
10
|
+
date: 2026-08-17 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: base64
|