Skip to content

Commit 2ad525d

Browse files
fix(core): add additional checks in verifyUniqueWithinMutation for pruned mutations (#9478)
**Description** Cherry picked from main (original PR #9450) into release/v24.1 This PR adds additional checks in edgraph/server.go::verifyUniqueWithinMutation for mutations that may have been conditionally pruned in updateMutations (called just before). Also a basic test verifies not only the prevention of the panic but also assures unpruned mutations were applied. Note this is an improvement upon #9449 in that additional range and inner loop predicate checks were required (@gooohgb). **Checklist** - [x] Code compiles correctly and linting passes locally - [x] For all _code_ changes, an entry added to the `CHANGELOG.md` file describing and linking to this PR - [x] Tests added for new functionality, or regression tests for bug fixes added as applicable
1 parent 291fc12 commit 2ad525d

File tree

12 files changed

+241
-96
lines changed

12 files changed

+241
-96
lines changed

.github/workflows/ci-dgraph-core-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ permissions:
2525
jobs:
2626
dgraph-core-tests:
2727
if: github.event.pull_request.draft == false
28-
runs-on: warp-ubuntu-latest-x64-4x
28+
runs-on: warp-ubuntu-latest-x64-16x
2929
timeout-minutes: 30
3030
steps:
3131
- uses: actions/checkout@v4

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project will
66
adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`.
77

8+
## Unreleased
9+
10+
- **Fixed**
11+
- fix(core): fix panic in verifyUniqueWithinMutation when mutation is conditionally pruned (#9450)
12+
813
## [v24.1.1] - 2025-03-20
914

1015
[v24.1.1]: https://github.com/hypermodeinc/dgraph/compare/v24.1.0...v24.1.1
@@ -29,6 +34,8 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`.
2934
- **Core**
3035
- Adds support for BigFloat https://github.com/hypermodeinc/dgraph/pull/3981
3136
- **GraphQL**
37+
- feat(graphql): adds translated DQL to extensions response when the "--graphql debug" super flag
38+
is set to true (#9280)
3239
- feat(graphql): adds @default directive to graphql (#8017)
3340
https://github.com/hypermodeinc/dgraph/pull/8837
3441
- feat(graphql): output translated DQL query when the graphql debug superflag is set
@@ -643,7 +650,6 @@ adhere to [Semantic Versioning](https://semver.org) starting `v22.0.0`.
643650

644651
- <details>
645652
<summary>CVE Fixes (31 total)</summary>
646-
647653
- CVE-2013-4235
648654
- CVE-2016-20013
649655
- CVE-2016-2781

dgraph/cmd/alpha/run_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1615,6 +1615,57 @@ func TestGeoValidWkbData(t *testing.T) {
16151615
require.Contains(t, string(resp.Json), `{"type":"Point","coordinates":[1,2]}`)
16161616
}
16171617

1618+
// TestWithConditionallyPrunedMutations provides a minimal test case to verify that the server
1619+
// does not panic when a mutation with a unique predicate is conditionally pruned.
1620+
func TestWithConditionallyPrunedMutations(t *testing.T) {
1621+
dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr)
1622+
require.NoError(t, err)
1623+
1624+
schema := `
1625+
username: string @index(term,hash) @upsert @unique .
1626+
type User {
1627+
username
1628+
}
1629+
`
1630+
require.NoError(t, alterSchemaWithRetry(schema))
1631+
ctx := context.Background()
1632+
1633+
// Create an initial user to act as an anchor for a conditional mutation.
1634+
initialMutation := &api.Mutation{
1635+
SetJson: []byte(`{"dgraph.type":["User"],"username":"user-A","uid":"_:user-A"}`),
1636+
CommitNow: true,
1637+
}
1638+
_, err = dg.NewTxn().Mutate(ctx, initialMutation)
1639+
require.NoError(t, err, "Setup mutation to create user-A failed")
1640+
1641+
// Send a request with one mutation to be pruned and one to be kept.
1642+
query := `query {
1643+
q(func: eq(username, "user-A")) { v as uid }
1644+
}`
1645+
1646+
mutations := []*api.Mutation{
1647+
{ // Mutation 0 (KEPT): This mutation should be processed correctly.
1648+
SetJson: []byte(`{"dgraph.type":["User"],"username":"user-C","uid":"_:user-C"}`),
1649+
},
1650+
{ // Mutation 1 (PRUNED): This mutation has a unique predicate but will be pruned.
1651+
SetJson: []byte(`{"dgraph.type":["User"],"username":"user-B","uid":"_:user-B"}`),
1652+
Cond: "@if(eq(len(v), 0))", // This is FALSE, so the mutation is pruned.
1653+
},
1654+
}
1655+
1656+
req := &api.Request{Query: query, Mutations: mutations, CommitNow: true}
1657+
resp, err := dg.NewTxn().Do(ctx, req)
1658+
1659+
require.NoError(t, err, "The main request failed, but it should have succeeded.")
1660+
require.NotNil(t, resp, "Response should not be nil")
1661+
1662+
// The kept mutation for user-C should have created a UID.
1663+
require.NotEmpty(t, resp.Uids["user-C"], "UID for kept user-C should not be empty")
1664+
1665+
// The pruned mutation for user-B should not have created a UID.
1666+
require.Empty(t, resp.Uids["user-B"], "UID for pruned user-B should be empty")
1667+
}
1668+
16181669
var addr string
16191670

16201671
type Token struct {

dgraphtest/image.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ func copyBinary(fromDir, toDir, version string) error {
183183
fromPath := filepath.Join(fromDir, binaryName)
184184
toPath := filepath.Join(toDir, "dgraph")
185185
if err := copy(fromPath, toPath); err != nil {
186-
return errors.Wrap(err, "error while copying binary into tempBinDir")
186+
return errors.Wrapf(err, "error while copying binary into tempBinDir [%v], from [%v]", toPath, fromPath)
187187
}
188188
return nil
189189
}

edgraph/server.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2151,18 +2151,26 @@ func verifyUniqueWithinMutation(qc *queryContext) error {
21512151

21522152
for i := range qc.uniqueVars {
21532153
gmuIndex, rdfIndex := decodeIndex(i)
2154-
if len(qc.gmuList[gmuIndex].Set) == 0 {
2155-
return nil
2154+
// handles cases where the mutation was pruned in updateMutations
2155+
if gmuIndex >= uint32(len(qc.gmuList)) || qc.gmuList[gmuIndex] == nil || rdfIndex >= uint32(len(qc.gmuList[gmuIndex].Set)) {
2156+
continue
21562157
}
21572158
pred1 := qc.gmuList[gmuIndex].Set[rdfIndex]
21582159
pred1Value := dql.TypeValFrom(pred1.ObjectValue).Value
21592160
for j := range qc.uniqueVars {
2161+
if i == j {
2162+
continue
2163+
}
21602164
gmuIndex2, rdfIndex2 := decodeIndex(j)
2165+
// check for the second predicate, which could also have been pruned
2166+
if gmuIndex2 >= uint32(len(qc.gmuList)) || qc.gmuList[gmuIndex2] == nil || rdfIndex2 >= uint32(len(qc.gmuList[gmuIndex2].Set)) {
2167+
continue
2168+
}
21612169
pred2 := qc.gmuList[gmuIndex2].Set[rdfIndex2]
21622170
if pred2.Predicate == pred1.Predicate && dql.TypeValFrom(pred2.ObjectValue).Value == pred1Value &&
21632171
pred2.Subject != pred1.Subject {
21642172
return errors.Errorf("could not insert duplicate value [%v] for predicate [%v]",
2165-
dql.TypeValFrom(pred1.ObjectValue).Value, pred1.Predicate)
2173+
pred1Value, pred1.Predicate)
21662174
}
21672175
}
21682176
}

edgraph/server_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/dgraph-io/badger/v4"
2929
"github.com/dgraph-io/dgo/v240/protos/api"
3030
"github.com/hypermodeinc/dgraph/v24/chunker"
31+
"github.com/hypermodeinc/dgraph/v24/dql"
3132
"github.com/hypermodeinc/dgraph/v24/schema"
3233
"github.com/hypermodeinc/dgraph/v24/worker"
3334
"github.com/hypermodeinc/dgraph/v24/x"
@@ -217,3 +218,82 @@ func TestGetHash(t *testing.T) {
217218
worker.Config.AclSecretKeyBytes = x.Sensitive("123456789")
218219
require.Equal(t, hex.EncodeToString(h.Sum(nil)), getHash(10, 20))
219220
}
221+
222+
func TestVerifyUniqueWithinMutationBoundsChecks(t *testing.T) {
223+
t.Run("gmuIndex out of bounds", func(t *testing.T) {
224+
qc := &queryContext{
225+
gmuList: []*dql.Mutation{
226+
{
227+
Set: []*api.NQuad{
228+
{
229+
Subject: "_:a",
230+
Predicate: "username",
231+
ObjectValue: &api.Value{
232+
Val: &api.Value_StrVal{StrVal: "user1"},
233+
},
234+
},
235+
},
236+
},
237+
},
238+
// Reference gmuList[1], which is out of bounds.
239+
uniqueVars: map[uint64]uniquePredMeta{
240+
encodeIndex(1, 0): {},
241+
},
242+
}
243+
err := verifyUniqueWithinMutation(qc)
244+
require.NoError(t, err)
245+
})
246+
247+
t.Run("rdfIndex out of bounds", func(t *testing.T) {
248+
qc := &queryContext{
249+
gmuList: []*dql.Mutation{
250+
{
251+
Set: []*api.NQuad{
252+
// Only one element at index 0.
253+
{
254+
Subject: "_:a",
255+
Predicate: "username",
256+
ObjectValue: &api.Value{
257+
Val: &api.Value_StrVal{StrVal: "user1"},
258+
},
259+
},
260+
},
261+
},
262+
},
263+
// Reference Set[1], which is out of bounds.
264+
uniqueVars: map[uint64]uniquePredMeta{
265+
encodeIndex(0, 1): {},
266+
},
267+
}
268+
err := verifyUniqueWithinMutation(qc)
269+
require.NoError(t, err)
270+
})
271+
272+
t.Run("gmuList element is nil", func(t *testing.T) {
273+
qc := &queryContext{
274+
gmuList: []*dql.Mutation{
275+
nil, // Element at index 0 is nil.
276+
},
277+
uniqueVars: map[uint64]uniquePredMeta{
278+
encodeIndex(0, 0): {},
279+
},
280+
}
281+
err := verifyUniqueWithinMutation(qc)
282+
require.NoError(t, err)
283+
})
284+
285+
t.Run("Set slice is nil", func(t *testing.T) {
286+
qc := &queryContext{
287+
gmuList: []*dql.Mutation{
288+
{
289+
Set: nil, // Set slice is nil.
290+
},
291+
},
292+
uniqueVars: map[uint64]uniquePredMeta{
293+
encodeIndex(0, 0): {},
294+
},
295+
}
296+
err := verifyUniqueWithinMutation(qc)
297+
require.NoError(t, err)
298+
})
299+
}

tlstest/tls/ca.crt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ wUnt9lSE8Rkcj8fssMuueIFsmcCSviOA3hBTW62imPKQa52KMiXgeP7bKdD1wwtm
1717
sCp0YNLG9zTehc0pwWFnZ87awRURdR8GPAWE/sdFXrOfe+0I9spXtqFOhUkvDDiV
1818
hF0KbXnwrkL9rH2Z7/fm4Xlct8p9q2Wj86gdeigtXj9jc52LrON8raTSZBRL2bgw
1919
NXzLIMDHkIBsygVmaSWCilJNhl8Cv4nfcuVkcxsvze0aotVbOObp
20-
-----END CERTIFICATE-----
20+
-----END CERTIFICATE-----

tlstest/tls/ca.key

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ AdTjnqKN8Tddy5iWaqeKOojFnpYMXBCnoEs458MPKyU9mQa55/9MFXy5ewuNO/7r
2424
jGv8QQKBgQC9FAnHP3cF6i4HSE91B9VkXmNKdrv55ITYzcYRUl8GPIXnQKZUGbQX
2525
OIcQGT6Oz+M8xVwG1jizBjcZ7f2c2PE5SrzSGrWchYjqfGEl5YHd044nkv0wqVnP
2626
i31yFInYZYGeLyevJjRpsPZxjQ0/QCQpro/tWjXbpkVREdX+3MK+bQ==
27-
-----END RSA PRIVATE KEY-----
27+
-----END RSA PRIVATE KEY-----

tlstest/tls/client.acl.crt

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
-----BEGIN CERTIFICATE-----
2-
MIIDSzCCAjOgAwIBAgIICqGW9WRZh6MwDQYJKoZIhvcNAQELBQAwRjEaMBgGA1UE
2+
MIIDSDCCAjCgAwIBAgIICpx5Zw6jC8IwDQYJKoZIhvcNAQELBQAwRjEaMBgGA1UE
33
ChMRRGdyYXBoIExhYnMsIEluYy4xFzAVBgNVBAMTDkRncmFwaCBSb290IENBMQ8w
4-
DQYDVQQFEwY2MzQwNTgwHhcNMjQwNDAyMDEwODQ4WhcNMjUwNDAzMDEwODQ4WjA7
5-
MRowGAYDVQQKExFEZ3JhcGggTGFicywgSW5jLjEMMAoGA1UEAxMDYWNsMQ8wDQYD
6-
VQQFEwYwYWExOTYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDK7tIa
7-
Rgz5IFX2dv+8phvaATKC+1XQZIzaf1umxakRr2Lqzc4NbJhE3+pX3PvL1m5R2ZbX
8-
N8DatxDVVO0D8aUivzU/Z6OOFKWngXTSdU4w4LNzTkB4EPsNRocItQ4X+H4jHHRu
9-
gD9yZWD2z+4oc/wQ9Oj2Raq0y0J45ZUFrIF25DzQHE1m76Cn701zgH3oZbmfDtBX
10-
eAU85PfIt3YBrGooxViWMzDGzP+i/3bBPg5uD44tVeJz2USTDGqv822ndC5PATGc
11-
b3YY8D+m+te6KGJw48I68PPNDk5uPk/KCwoXYXnZXL9Ykf0SA3CRIkNYtMmJGINm
12-
Y0vxEmUDNtbI3KHHAgMBAAGjSDBGMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAK
13-
BggrBgEFBQcDAjAfBgNVHSMEGDAWgBS0Ifn5zbkosHJ2Wn8CJzIeFgcPAjANBgkq
14-
hkiG9w0BAQsFAAOCAQEAdfOYM/bHm1lgDU77jxQcIsnVMhxSVbCuCy8JZQ6m6HSs
15-
kNCWS7UKkW14uaVwRT78Q8O0J1E2TrGqS4b6llZhxsbRRBMSttofU581DYaicZ2N
16-
hU+Z0q/YMC5jKliTBWFvcZ/KnGqRBDgE/gwotEecnVEpc4i1qCUosleN8qgy4EDp
17-
E4XsCgMtzOWuH2omZ0Lkfc5NZ4071zIFHc4Uc7eOmThyZyMRrpL1jXwYNYFS2MMG
18-
GTG07HsszXZSTue7Cq9DkHLerA7yZI8K4KzmyLO1gipobbUFBlH2NIaUGYvaRj+H
19-
1V4JqTjrcrZhCAgq+Sdm/vTXc79Rei77VShjbvMhOw==
20-
-----END CERTIFICATE-----
4+
DQYDVQQFEwY2MzQwNTgwHhcNMjUwNDAyMTgxMDI5WhcNMzMwNjIwMTgxMDI5WjA4
5+
MRcwFQYDVQQKEw5IeXBlcm1vZGUgSW5jLjEMMAoGA1UEAxMDYWNsMQ8wDQYDVQQF
6+
EwYwYTljNzkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC963UWeFQK
7+
4vRmOOHaJkM6MLD2LwV6rvLBfINJf202UAtS9Pn89G5DsYl+kjyTC8VD+gTc3ke9
8+
7yjnIvBA2Qd71LIcYJX8Hi2gtVMIRqSpMFRDoP5dccEQC6CGe30KLZnYFxxrZWDY
9+
sBltiqzCKEiPC/LkeoTUJDn3dtddJhfknxptWlMvq4PKnhgbU4i0QXAytj4LvXku
10+
Q5uZnYw1IUpR9eNZh4tNbYygRKOV8vE/rfXpFq4bg+X9ydn86pGYCy6E5TJaJMAO
11+
elTr/GppzEM9ZjszQB+9RZW0qkK63JKIY0g11qOw4xnOvvTEZ3RsCpjdbcYDABoy
12+
W0o9J2dtcEf/AgMBAAGjSDBGMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggr
13+
BgEFBQcDAjAfBgNVHSMEGDAWgBS0Ifn5zbkosHJ2Wn8CJzIeFgcPAjANBgkqhkiG
14+
9w0BAQsFAAOCAQEAcxGPCJbIra6zC+8GTnFhVIk6XpzdkoGi72tCXq7UOeJbzFXm
15+
mdI9NxStUd7E+R/fu2lRCnuZTaNrTamyBOTThbDGftyZOhJbprVzLP+bdiS7a5fJ
16+
GgMt9yQA0N9ijFgbie+yEVy86zoptnXGbMb0BNMcZPevlHf7SdRPX6YTK9HQIEC2
17+
Ggj2SQb2qBeP9znhW5JTIzO43mSKFjI+FOZAvEDwQH11kmuQkoQNoYQ//gHS4CnC
18+
Enq+Kz9jmdIVITedvqrlai50R7+MHCzglAcrTrcsBvlsjLhmTUL4oM2gEI8oCZ2v
19+
dahQcGiJdO7ZX/1tOLi6r0WrovezFFbVN8ijag==
20+
-----END CERTIFICATE-----

tlstest/tls/client.acl.key

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
-----BEGIN RSA PRIVATE KEY-----
2-
MIIEpAIBAAKCAQEAyu7SGkYM+SBV9nb/vKYb2gEygvtV0GSM2n9bpsWpEa9i6s3O
3-
DWyYRN/qV9z7y9ZuUdmW1zfA2rcQ1VTtA/GlIr81P2ejjhSlp4F00nVOMOCzc05A
4-
eBD7DUaHCLUOF/h+Ixx0boA/cmVg9s/uKHP8EPTo9kWqtMtCeOWVBayBduQ80BxN
5-
Zu+gp+9Nc4B96GW5nw7QV3gFPOT3yLd2AaxqKMVYljMwxsz/ov92wT4Obg+OLVXi
6-
c9lEkwxqr/Ntp3QuTwExnG92GPA/pvrXuihicOPCOvDzzQ5Obj5PygsKF2F52Vy/
7-
WJH9EgNwkSJDWLTJiRiDZmNL8RJlAzbWyNyhxwIDAQABAoIBAERJSs/mhJUMSYDv
8-
GC095B9eycWDl6PRQgiAVLHwz2yRDQMgD9c6lJ6QjoXUabJ0zJikVHrFWxL+CR8Q
9-
PdVW6g+gMeXNFR9JWVjdWhc7u/z8j8YcLdt5M1GLSwMmEcEFMETWXWOVqkPvU0qa
10-
O3THWqnGD+JXHRNLVmniL1xCaSo9F2VvepKRwrR0WqcErPU0BiECVzbt3q+7JxYL
11-
QbcMKv+tkraVDfgBt66TaHAofCDLDp0OICZumnNxBzfBd+fZz+LKnwsrdfF26FN/
12-
3PP0bsd4Y8lqlfFqvmdvcug7DclrW6ulCXPbPoHO6+IAn3AfLvd7jEsfKwhJKJU4
13-
ytjsmgkCgYEA1FJgl3O3025GyZ6m8NBzKPgiqybY5FrL094D+OFezHdE44bRhw/0
14-
lapN94i4RELKBgN+Rt867Il2TfWxKCMSfzZfE4c/YG4ud0xj6F3n77h2Ve1zZRli
15-
nXWa9zbC6Gf5T6oSHXTwBHjF6SvzrsgyqwcMncB9Y0ySfDt6K2APkVsCgYEA9K39
16-
pXcwotcpCrpy0S2e4FHximOI+PR27Toz/CRJRh8UE6P+sei3+9Pb3hECLpcNrd3A
17-
kAwDhig3IuHtrGkGG75rJF2Gh/PbVcwnAGdSWfMAXuujkh1bbtMTM1bcShOwrnBt
18-
/Yzi/EoZ1i6stkozWuwGcZdS/bfZP8Htre6DUQUCgYEAhhXwAjWGJfRCqBmVfAdo
19-
crshycyitLPSfV58S7SoVaAM1VPTBuTApbQDwQuKszEqvFmAaemYIME/AUTkOGDN
20-
0aDYslB7ab6rgZOwlJKZYv6vXNy0P2ikqqQW1YCz3mmrPPA+uHOdEHXlJrgnEH5F
21-
4VR2Vzm09fzplaNKKBJmQnkCgYEA126GgPTTvtzgxxBo3aJkW6/qkuWWiVR9jdwM
22-
P3oXhgUBIVXof+6eZ70evoSNPkZDhg1UacAaLjokabMxF/eUqELdhTRPqk5clUte
23-
H63ZRVd+wRSKfbEZexeftTlKVnLFwqE9RtyQUoBQBVQtP4dPdkOQXN1W9gu03b07
24-
L0RtHJ0CgYA8MSme2TZBzLuhPFxTDQVcBJbpYXGldQ+SSXe/yL05oCZ7CW0kZn5Y
25-
y4zh7UH7A0JTep8KYlj5SgEO9zHngP/s7Xp0znuOZdGLmhG+erZGEqSH/Uto+Wml
26-
+rFdJFoey49JtdBRxLIBYFyF4fyv4tKsqlzJN3et1jboG0JCpnI4Ow==
2+
MIIEogIBAAKCAQEAvet1FnhUCuL0Zjjh2iZDOjCw9i8Feq7ywXyDSX9tNlALUvT5
3+
/PRuQ7GJfpI8kwvFQ/oE3N5Hve8o5yLwQNkHe9SyHGCV/B4toLVTCEakqTBUQ6D+
4+
XXHBEAughnt9Ci2Z2Bcca2Vg2LAZbYqswihIjwvy5HqE1CQ593bXXSYX5J8abVpT
5+
L6uDyp4YG1OItEFwMrY+C715LkObmZ2MNSFKUfXjWYeLTW2MoESjlfLxP6316Rau
6+
G4Pl/cnZ/OqRmAsuhOUyWiTADnpU6/xqacxDPWY7M0AfvUWVtKpCutySiGNINdaj
7+
sOMZzr70xGd0bAqY3W3GAwAaMltKPSdnbXBH/wIDAQABAoIBADczAE0PnB2t6Srs
8+
zXgCNvfqvwROlmEaCCSEdAQgrTVj/maWHTKQOJwkNbo79q8mdKdVO6SWVnO110WW
9+
ug2fBrXPFBV7XRxyCQD0srVtEt651UPyvvuZ3Gs1deqgaa9ToatUzEXKqWNRi5Jz
10+
A9NKsgwhhO8zTPf7r31lZSMFt1wTAe5Flcf//cVCCPvHhpCmaKf6P3iQEHP06PeA
11+
KZiHjD3r6lDVAT5RHWAB3wejfZkH5vZIlFh57E5e/T8iQC3xJxDzFngVxno8+jXT
12+
V73LPhQNWFr5IXZwJThK3wrVUU3MPoqZUswnVgbZOSht0+pTyNZ/xv4gnELpuLw/
13+
OmoGPIECgYEA5SB2mspk6fQaKHm6tdseO+ixkx4lYQpU2dVGluJRyBHbc4lpwXs6
14+
AklrvOtpuVTQuHS0UNaAHwA4MMQmVKzuAh6PzQVDaipI9++DCmvEavCVYMiYJCEj
15+
983+zPlY0jUjCNFZZNHrwgOq92T78CMSTv0dER+U3GrfMxM5ZpkBEfcCgYEA1DHN
16+
MdDS1YwdmkqbsfIDyp7rFfplHf/zGVWwSnfjjO2+E2QtdUR2C1h4XOpeJBLJdPVN
17+
5o5B90MWuDEjuRlUJ7+LYKpoSiv344+XZ7fjjzKJFF1N0sJHTnCBpPyyydo4BJiH
18+
mjABhyAigSrbffB8mbs2nQwsazZBVjJRC5CU+DkCgYBj0035SJ7I00ImSkrPeoTk
19+
ow/iweqVKdQoB+S2TipFWYpb/w1lb5OpdWWMLKCmT06UXrxEz/+IAid5D6yHjcQw
20+
hjIup9FkSsH5y48zFhrAYMskHKE0dH/gMg6GLUjGZMI70EH7S/5ETCbLnGKrkAwk
21+
/+APGsxXb/SIoyKKEbVGpwKBgHWNCVG5j7MBpjydbFdN7U/Ibg+E4ZOXAUNdtwXq
22+
GMMvyYbQjmZSgNqULkm7BGATkEscwgUhdeXW4rUzc8jNAVPrxvjMLm44oWENYSDy
23+
PDxOEKaZcc16FyqbJRcTbrBoFXRmU2jVg2MqKDNKXMIOAdjgX8bswYa7TcLEqSUw
24+
+XXxAoGAD9DlvWnKv2BvuQHk+Fzs+3T0s7XnLQAm32w8t/suipFvZO7rh7p3BKqW
25+
WlKFw3RIlepOcfvvOm5DdP6DLGd5WmiaALuUx9BSixdo8iXRD21paaDDPQ999jDo
26+
Xy8ia79Bw77RFsVI17H8xNhXZ4IvtWeMvPUJo7YthGbRLYNsyJI=
2727
-----END RSA PRIVATE KEY-----

0 commit comments

Comments
 (0)