json2sql 1.0.8 → 1.0.10
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/json2sql/input_policy.rb +66 -23
- data/lib/json2sql/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eb3184ddebd15d9412944f3e518524bbb0ae82b37dfcc925acb4c907e1244c3a
|
|
4
|
+
data.tar.gz: 0575b7e23355d4c08954cdf08a8da924bb8fccf67cd77086bb191c10b35a5780
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3501e46e1a5f0d10abe56a545f965e6c501864e436064f1cbe69e7e3573e35d177ae41b379df5e90ca77baf8d9ca19ea2b3843d21bdbc22b8e5bd21aab86f125
|
|
7
|
+
data.tar.gz: 5df88c1b1c985fc920baea0717746e7348c6040ec2d3ef8309a0f52dbb3c72a8f2cdc4e1e520e347bcceb6001ee59206680f90f7663522d0ba40b718ef82a8d7
|
|
@@ -7,19 +7,33 @@ module Json2sql
|
|
|
7
7
|
# Tables absent from `tables:` are blocked entirely.
|
|
8
8
|
# Empty `tables:` = no restriction.
|
|
9
9
|
# :deny — all tables pass; only listed columns are stripped.
|
|
10
|
-
# tables: Per-table configuration Hash:
|
|
11
|
-
# { table_name => { columns: [...],
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
10
|
+
# tables: Per-table configuration Hash (recursive):
|
|
11
|
+
# { table_name => { columns: [...],
|
|
12
|
+
# children: { child_table => { columns: [...], ... } },
|
|
13
|
+
# parents: { parent_table => { columns: [...], ... } },
|
|
14
|
+
# where: { "and" => { col => val } } } }
|
|
15
|
+
# columns: column list — filtered by `mode` (allowed or denied).
|
|
16
|
+
# nil or absent = no column restriction for that table.
|
|
17
|
+
# children: nested hash of allowed/denied child tables with their own config.
|
|
18
|
+
# nil or absent = no restriction on children.
|
|
19
|
+
# In :deny mode: use empty hash {} to deny the relation entirely;
|
|
20
|
+
# a non-empty config applies column/where filtering without blocking.
|
|
21
|
+
# parents: nested hash of allowed/denied parent tables with their own config.
|
|
22
|
+
# nil or absent = no restriction on parents.
|
|
23
|
+
# In :deny mode: same rules as children.
|
|
24
|
+
# where: server-side conditions merged into "and". Forced keys overwrite
|
|
25
|
+
# user-supplied values — primary IDOR guard.
|
|
16
26
|
#
|
|
17
27
|
# Usage:
|
|
18
28
|
# policy = Json2sql::InputPolicy.new(
|
|
19
29
|
# mode: :allow,
|
|
20
30
|
# tables: {
|
|
21
|
-
# orders: {
|
|
22
|
-
#
|
|
31
|
+
# orders: {
|
|
32
|
+
# columns: %w[id total status],
|
|
33
|
+
# children: { order_items: { columns: %w[id price] } },
|
|
34
|
+
# parents: { users: { columns: %w[id name] } },
|
|
35
|
+
# where: { "and" => { "user_id" => 42 } }
|
|
36
|
+
# }
|
|
23
37
|
# }
|
|
24
38
|
# )
|
|
25
39
|
# safe_input = policy.apply(raw_params)
|
|
@@ -43,7 +57,7 @@ module Json2sql
|
|
|
43
57
|
|
|
44
58
|
input = filter_tables(input)
|
|
45
59
|
|
|
46
|
-
input.each { |table, params| sanitize_table(params, table) }
|
|
60
|
+
input.each { |table, params| sanitize_table(params, @tables[table] || {}) }
|
|
47
61
|
|
|
48
62
|
input
|
|
49
63
|
end
|
|
@@ -57,19 +71,52 @@ module Json2sql
|
|
|
57
71
|
input.select { |table, _| @tables.key?(table) }
|
|
58
72
|
end
|
|
59
73
|
|
|
60
|
-
def sanitize_table(params,
|
|
74
|
+
def sanitize_table(params, config)
|
|
61
75
|
|
|
62
76
|
return unless params.is_a?(Hash)
|
|
63
77
|
|
|
64
|
-
filter_columns(params,
|
|
78
|
+
filter_columns(params, config)
|
|
65
79
|
|
|
66
|
-
inject_where(params,
|
|
80
|
+
inject_where(params, config)
|
|
67
81
|
|
|
68
82
|
%w[children parents].each do |relation|
|
|
69
83
|
|
|
84
|
+
filter_relations(params, config, relation)
|
|
85
|
+
|
|
70
86
|
next unless params[relation].is_a?(Hash)
|
|
71
87
|
|
|
72
|
-
|
|
88
|
+
relation_configs = config[relation].is_a?(Hash) ? config[relation] : {}
|
|
89
|
+
|
|
90
|
+
params[relation].each { |child_table, child_params| sanitize_table(child_params, relation_configs[child_table] || {}) }
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Filters children/parents relations using mode.
|
|
95
|
+
# In :allow mode, only relations present as keys in config[relation_key] pass.
|
|
96
|
+
# In :deny mode:
|
|
97
|
+
# - relation config is nil or {} → relation is denied entirely.
|
|
98
|
+
# - relation config is a non-empty Hash → relation passes; sub-config is
|
|
99
|
+
# applied recursively (column filtering, where injection, etc.).
|
|
100
|
+
# If config[relation_key] is absent or not a Hash, relations are untouched.
|
|
101
|
+
|
|
102
|
+
def filter_relations(params, config, relation_key)
|
|
103
|
+
|
|
104
|
+
relations = params[relation_key]
|
|
105
|
+
|
|
106
|
+
return unless relations.is_a?(Hash)
|
|
107
|
+
|
|
108
|
+
relation_config = config[relation_key]
|
|
109
|
+
|
|
110
|
+
return unless relation_config.is_a?(Hash)
|
|
111
|
+
|
|
112
|
+
params[relation_key] = if @mode == :deny
|
|
113
|
+
|
|
114
|
+
relations.reject { |t, _| relation_config.key?(t) && (relation_config[t].nil? || (relation_config[t].is_a?(Hash) && relation_config[t].empty?)) }
|
|
115
|
+
|
|
116
|
+
else
|
|
117
|
+
|
|
118
|
+
relations.select { |t, _| relation_config.key?(t) }
|
|
119
|
+
|
|
73
120
|
end
|
|
74
121
|
end
|
|
75
122
|
|
|
@@ -78,27 +125,23 @@ module Json2sql
|
|
|
78
125
|
# Hash entries (function columns) always pass through in :allow mode.
|
|
79
126
|
# If no column list is defined for the table, columns are untouched.
|
|
80
127
|
|
|
81
|
-
def filter_columns(params,
|
|
128
|
+
def filter_columns(params, config)
|
|
82
129
|
|
|
83
130
|
columns = params["columns"]
|
|
84
131
|
|
|
85
132
|
return unless columns.is_a?(Array) || columns.is_a?(Hash)
|
|
86
133
|
|
|
87
|
-
list =
|
|
134
|
+
list = config["columns"]
|
|
88
135
|
|
|
89
136
|
return unless list.is_a?(Array)
|
|
90
137
|
|
|
91
138
|
params["columns"] = if @mode == :deny
|
|
92
139
|
|
|
93
|
-
columns.is_a?(Array)
|
|
94
|
-
? columns.reject { |c| list.include?(c) } \
|
|
95
|
-
: columns.reject { |k, _| list.include?(k) }
|
|
140
|
+
columns.is_a?(Array) ? columns.reject { |c| list.include?(c) } : columns.reject { |k, _| list.include?(k) }
|
|
96
141
|
|
|
97
142
|
else
|
|
98
143
|
|
|
99
|
-
columns.is_a?(Array)
|
|
100
|
-
? columns.select { |c| c.is_a?(Hash) || list.include?(c) } \
|
|
101
|
-
: columns.select { |k, _| list.include?(k) }
|
|
144
|
+
columns.is_a?(Array) ? columns.select { |c| c.is_a?(Hash) || list.include?(c) } : columns.select { |k, _| list.include?(k) }
|
|
102
145
|
|
|
103
146
|
end
|
|
104
147
|
end
|
|
@@ -107,9 +150,9 @@ module Json2sql
|
|
|
107
150
|
# Forced keys overwrite user-supplied values for the same column,
|
|
108
151
|
# preventing IDOR (e.g. attacker cannot override user_id).
|
|
109
152
|
|
|
110
|
-
def inject_where(params,
|
|
153
|
+
def inject_where(params, config)
|
|
111
154
|
|
|
112
|
-
forced_and =
|
|
155
|
+
forced_and = config.dig("where", "and")
|
|
113
156
|
|
|
114
157
|
return unless forced_and.is_a?(Hash)
|
|
115
158
|
|
data/lib/json2sql/version.rb
CHANGED