このコミットが含まれているのは:
@@ -354,15 +354,25 @@ RSpec.describe 'Counts table API', type: :request do
|
||||
expect(result['cells'].sum { _1['count'] }).to eq(4)
|
||||
expect(response.body).not_to include('hidden title')
|
||||
masked = query(mode: 'summary', rows: 'resource_id', cols: '')
|
||||
expect(masked['cells'].map { _1['values'] }).to eq([[CountsAxes::MASKED_VALUE]])
|
||||
expect(masked['cells'].map { _1['values'] }).to eq([[record.id]])
|
||||
expect(masked['cells'].first['count']).to eq(4)
|
||||
expect(query['versions']).to be_empty
|
||||
end
|
||||
|
||||
it 'rejects private summary predicates instead of exposing a count oracle' do
|
||||
get '/counts/query', params: base.merge(mode: 'summary', resource_id: record.id.to_s)
|
||||
expect(response).to have_http_status(:forbidden)
|
||||
expect(json).to eq('error' => 'private_condition')
|
||||
it 'allows public identity predicates without exposing private snapshot fields' do
|
||||
PostVersion.create!(post: record, version_no: 4, event_type: 'discard',
|
||||
url: record.url, title: 'hidden title', tags: '', tags_json: [],
|
||||
parent_post_ids: '', created_at: Time.utc(2024, 3))
|
||||
|
||||
get '/counts/query', params: base.merge(mode: 'summary',
|
||||
resource_id: record.id.to_s, rows: 'resource_id', cols: 'title')
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json['count']).to eq(4)
|
||||
expect(json['cells'].sum { _1['count'] }).to eq(4)
|
||||
expect(json['cells'].map { _1['values'].last })
|
||||
.to include(CountsAxes::MASKED_VALUE)
|
||||
expect(response.body).not_to include('hidden title')
|
||||
end
|
||||
|
||||
it 'reports the combined axis limit without replacing it with a generic 400' do
|
||||
|
||||
@@ -142,7 +142,7 @@ RSpec.describe 'Counts masked pivot contracts' do
|
||||
expect(page[:row_headers]).to include(hash_including(values: [nil], kind: 'data'))
|
||||
end
|
||||
|
||||
it 'sorts public numeric values numerically alongside a masked group' do
|
||||
it 'sorts public version numbers numerically including formerly hidden records' do
|
||||
post = PostVersion.order(:id).last.post
|
||||
[2, 10].each do |number|
|
||||
PostVersion.create!(post:, version_no: number, event_type: 'update',
|
||||
@@ -151,22 +151,47 @@ RSpec.describe 'Counts masked pivot contracts' do
|
||||
end
|
||||
page = result(nil, 'rows' => 'version_no', 'cols' => '')
|
||||
values = page[:row_headers].select { _1[:kind] == 'data' }.map { _1[:values].first }
|
||||
expect(values.reject { _1 == CountsAxes::MASKED_VALUE }.map(&:to_i)).to eq([1, 2, 10])
|
||||
expect(values.last).to eq(CountsAxes::MASKED_VALUE)
|
||||
expect(values.map(&:to_i)).to eq([1, 2, 10])
|
||||
end
|
||||
|
||||
it 'rejects private predicates and supplied measure coordinates before SQL labels escape' do
|
||||
probes = [{ 'resource_id' => PostVersion.first.post_id.to_s },
|
||||
{ 'tags' => 'A' },
|
||||
{ 'conditions' => '', 'conditions.0.field' => 'title', 'conditions.0.operator' => 'eq',
|
||||
'conditions.0.value' => 'hidden-title-0' },
|
||||
{ 'drill' => '', 'drill.0.field' => 'tags', 'drill.0.value_type' => 'string',
|
||||
'drill.0.value' => "internal:#{ tags.first.id }" },
|
||||
it 'uses public identity fields and masked values for unauthorised filters' do
|
||||
hidden_id = PostVersion.first.post_id.to_s
|
||||
base = { 'rows' => '', 'cols' => '' }
|
||||
condition = lambda do |field, operator, value|
|
||||
{ 'conditions' => '', 'conditions.0.field' => field,
|
||||
'conditions.0.operator' => operator, 'conditions.0.value' => value }
|
||||
end
|
||||
|
||||
expect(result(nil, base.merge('resource_id' => hidden_id))[:cells].sole[:count]).to eq(2)
|
||||
expect(result(nil, base.merge(condition.call('version_no', 'eq', '2')))[:cells]
|
||||
.sole[:count]).to eq(2)
|
||||
expect(result(nil, base.merge(condition.call('user', 'eq', member.id.to_s)))[:cells]
|
||||
.sole[:count]).to eq(5)
|
||||
expect(result(nil, base.merge(condition.call('title', 'eq', 'hidden-title-0')))[:cells]
|
||||
.sole[:count]).to eq(0)
|
||||
expect(result(nil, base.merge(condition.call('title', 'ne', 'hidden-title-0')))[:cells]
|
||||
.sole[:count]).to eq(5)
|
||||
expect(result(nil, base.merge(condition.call('title', 'eq', '********')))[:cells]
|
||||
.sole[:count]).to eq(4)
|
||||
expect(result(nil, base.merge(condition.call('video_ms', 'eq', '********')))[:cells]
|
||||
.sole[:count]).to eq(4)
|
||||
expect(result(nil, base.merge(condition.call('title', 'like', '%title%')))[:cells]
|
||||
.sole[:count]).to eq(1)
|
||||
expect(result(nil, base.merge(condition.call('title', 'like', '%')))[:cells]
|
||||
.sole[:count]).to eq(5)
|
||||
expect(result(nil, base.merge('tags' => 'C'))[:cells].sole[:count]).to eq(1)
|
||||
expect(result(nil, base.merge('tags' => 'not:C'))[:cells].sole[:count]).to eq(4)
|
||||
end
|
||||
|
||||
it 'rejects server-only coordinates in column order and drilldown' do
|
||||
probes = [
|
||||
{ 'cols' => 'tags', 'column_order' => '', 'column_order.0.value_type' => 'string',
|
||||
'column_order.0.value' => CountsAxes::MASKED_VALUE }]
|
||||
'column_order.0.value' => CountsAxes::MASKED_VALUE },
|
||||
{ 'drill' => '', 'drill.0.field' => 'tags', 'drill.0.value_type' => 'string',
|
||||
'drill.0.value' => CountsAxes::MASKED_VALUE }]
|
||||
HistoryAccess.set(user: nil) do
|
||||
probes.each do |probe|
|
||||
expect { CountsTableQuery.new(params.merge(probe)) }
|
||||
expect { CountsTableQuery.new(params.merge(probe)).call }
|
||||
.to raise_error(CountsAxes::PrivateCondition)
|
||||
end
|
||||
end
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする