oydid 0.9.4 → 0.9.5
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/VERSION +1 -1
- data/lib/oydid/log.rb +15 -0
- data/lib/oydid.rb +9 -0
- data/spec/oydid_spec.rb +31 -0
- 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: 3aa2cc9a77716310c41f0bda9dae80ca4939602b778384c79d874e63e8a27000
|
|
4
|
+
data.tar.gz: cef9d037771c87c0bb4fb3614696c53c6a4b9d358f016f074defc37d88c810bd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 43b1ef66a0a911c316a9c0a20d5018595ece344545faf4b986040210b10245f3557da15c5ade1681402ad816d918c75fd788fae5d788a5ba8a2469a39e5b7ee1
|
|
7
|
+
data.tar.gz: c08146faf1769b6f782e50bbd23468629e7a9069c9e20e9c6bee90cf0dce9a68f494aeceeb57b23d972bd331a73c1643a58bcac91cb73df2238eff33df0061e5
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.9.
|
|
1
|
+
0.9.5
|
data/lib/oydid/log.rb
CHANGED
|
@@ -90,6 +90,21 @@ class Oydid
|
|
|
90
90
|
end
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
+
# collapse byte-identical log entries (keep first), keyed on the entry hash
|
|
94
|
+
# that `previous` references resolve to. A replayed CREATE or tangling
|
|
95
|
+
# TERMINATE would otherwise trip dag_did's CREATE/terminate counts and make
|
|
96
|
+
# the DID unresolvable - reachable through the unauthenticated append path
|
|
97
|
+
# with no signing key. Genuinely distinct fork entries (different hash) are
|
|
98
|
+
# kept and still fail closed as ambiguous.
|
|
99
|
+
def self.dedup_log(logs)
|
|
100
|
+
return logs unless logs.is_a?(Array)
|
|
101
|
+
seen = {}
|
|
102
|
+
logs.select do |el|
|
|
103
|
+
key = (canonical(el.slice("ts","op","doc","sig","previous")) rescue el.inspect)
|
|
104
|
+
seen.key?(key) ? false : (seen[key] = true)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
93
108
|
def self.dag_did(logs, options)
|
|
94
109
|
dag = DAG.new
|
|
95
110
|
dag_log = []
|
data/lib/oydid.rb
CHANGED
|
@@ -142,6 +142,14 @@ class Oydid
|
|
|
142
142
|
log_hash = hash_split[0]
|
|
143
143
|
log_location = hash_split[1]
|
|
144
144
|
end
|
|
145
|
+
# D11: the log reference may carry its location as %40 (the
|
|
146
|
+
# W3C-conform form of @); split on both, exactly as the DID split
|
|
147
|
+
# above does.
|
|
148
|
+
if log_hash.include?(CGI.escape LOCATION_PREFIX)
|
|
149
|
+
hash_split = log_hash.split(CGI.escape LOCATION_PREFIX)
|
|
150
|
+
log_hash = hash_split[0]
|
|
151
|
+
log_location = hash_split[1]
|
|
152
|
+
end
|
|
145
153
|
end
|
|
146
154
|
if log_location == ""
|
|
147
155
|
log_location = DEFAULT_LOCATION
|
|
@@ -152,6 +160,7 @@ class Oydid
|
|
|
152
160
|
if log_array.nil?
|
|
153
161
|
return [nil, msg]
|
|
154
162
|
else
|
|
163
|
+
log_array = dedup_log(log_array)
|
|
155
164
|
if options[:trace]
|
|
156
165
|
puts " .. Log retrieved"
|
|
157
166
|
end
|
data/spec/oydid_spec.rb
CHANGED
|
@@ -646,6 +646,37 @@ describe "OYDID handling" do
|
|
|
646
646
|
end
|
|
647
647
|
end
|
|
648
648
|
|
|
649
|
+
# SECURITY regression: the append endpoint is unauthenticated, so a byte-identical
|
|
650
|
+
# log entry can be replayed with no signing key. A duplicated CREATE trips dag_did's
|
|
651
|
+
# "wrong number of CREATE entries" and a duplicated tangling TERMINATE its terminate
|
|
652
|
+
# count, making the DID unresolvable. dedup_log collapses byte-identical replays
|
|
653
|
+
# (keep-first) at ingestion; genuinely distinct fork entries survive and still fail
|
|
654
|
+
# closed as ambiguous.
|
|
655
|
+
describe "dedup_log collapses byte-identical replayed entries" do
|
|
656
|
+
let(:create_e) { { "ts" => 1, "op" => 2, "doc" => "zCreate", "sig" => "z", "previous" => [] } }
|
|
657
|
+
let(:term_e) { { "ts" => 1, "op" => 0, "doc" => "zTerm", "sig" => "z", "previous" => [] } }
|
|
658
|
+
|
|
659
|
+
it "removes a byte-identical duplicate, keeping the first" do
|
|
660
|
+
deduped = Oydid.dedup_log([create_e, term_e, create_e.dup])
|
|
661
|
+
expect(deduped.length).to eq 2
|
|
662
|
+
expect(deduped.count { |e| e["op"] == 2 }).to eq 1
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
it "lets a de-duplicated log resolve where the raw one fails" do
|
|
666
|
+
logs = [create_e, term_e, create_e.dup]
|
|
667
|
+
expect(Oydid.dag_did(logs, { silent: true }).last).to match(/wrong number of CREATE/)
|
|
668
|
+
dag, create_index, _t, msg = Oydid.dag_did(Oydid.dedup_log(logs), { silent: true })
|
|
669
|
+
expect(msg).to eq ""
|
|
670
|
+
expect(dag).not_to be_nil
|
|
671
|
+
expect(create_index).to eq 0
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
it "keeps genuinely distinct entries (a real fork is not collapsed)" do
|
|
675
|
+
fork = create_e.merge("doc" => "zCreate2")
|
|
676
|
+
expect(Oydid.dedup_log([create_e, fork, term_e]).length).to eq 3
|
|
677
|
+
end
|
|
678
|
+
end
|
|
679
|
+
|
|
649
680
|
# a broken repository (5xx) has to stay distinguishable from a DID that is
|
|
650
681
|
# not stored there - otherwise the caller reports "not found" and thereby
|
|
651
682
|
# claims the identifier never existed
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: oydid
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Christoph Fabianek
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-09-
|
|
10
|
+
date: 2026-09-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: simple_dag
|