tinyrelayfile
tinyrelay/internal/daemon/git_private_restart_test.go/

internal/daemon/git_private_restart_test.go raw
1package daemon
2
3import (
4 "context"
5 "net/http"
6 "net/http/httptest"
7 "strings"
8 "testing"
9
10 "github.com/FelineStateMachine/tinyrelay/internal/event"
11 "github.com/FelineStateMachine/tinyrelay/internal/relay"
12)
13
14func TestPrivateSignedGitStateRemainsHiddenAfterRestart(t *testing.T) {
15 ctx := context.Background()
16 dir := t.TempDir()
17 secret := strings.Repeat("0", 63) + "1"
18 owner, err := event.PublicKey(secret)
19 if err != nil {
20 t.Fatal(err)
21 }
22 app, err := New(ctx, Config{DataDir: dir, DefaultTenant: "main"})
23 if err != nil {
24 t.Fatal(err)
25 }
26 meta, err := app.Create(ctx, CreateOptions{Name: "main", Owner: owner, Template: "default"})
27 if err != nil {
28 t.Fatal(err)
29 }
30 tenant, err := app.tenant(ctx, meta, "http://relay.test")
31 if err != nil {
32 t.Fatal(err)
33 }
34 p := tenant.Policy()
35 p.Features.Grasp = true
36 p.Features.Grasp08 = true
37 p.Reads = "members"
38 if err := tenant.applyPolicy(ctx, p); err != nil {
39 t.Fatal(err)
40 }
41 announcement := event.Event{Kind: event.KIND_REPO, CreatedAt: 1, Tags: [][]string{{"d", "private"}, {"private", "true"}, {"clone", "http://relay.test/" + "npub10xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqpkge6d/private.git"}, {"relays", "ws://relay.test"}}}
42 if err := event.Sign(&announcement, secret); err != nil {
43 t.Fatal(err)
44 }
45 if _, err := tenant.router.Publish(ctx, announcement, relay.Session{PubKeys: []string{owner}}); err != nil {
46 t.Fatal(err)
47 }
48 state := event.Event{Kind: event.KIND_REPO_STATE, CreatedAt: 2, Tags: [][]string{{"d", "private"}, {"HEAD", "ref: refs/heads/main"}, {"refs/heads/main", strings.Repeat("a", 40)}}}
49 if err := event.Sign(&state, secret); err != nil {
50 t.Fatal(err)
51 }
52 if _, err := tenant.router.Publish(ctx, state, relay.Session{PubKeys: []string{owner}}); err != nil {
53 t.Fatal(err)
54 }
55 if !tenant.git.IsPending(state.ID) {
56 t.Fatal("missing-object state was not held pending")
57 }
58 if err := app.Close(ctx); err != nil {
59 t.Fatal(err)
60 }
61
62 app, err = New(ctx, Config{DataDir: dir, DefaultTenant: "main"})
63 if err != nil {
64 t.Fatal(err)
65 }
66 if err := app.Start(ctx, "http://relay.test"); err != nil {
67 t.Fatal(err)
68 }
69 t.Cleanup(func() { _ = app.Close(ctx) })
70 restarted := app.tenants[meta.ID]
71 if restarted == nil || !restarted.git.IsPending(state.ID) {
72 t.Fatal("restart did not restore pending signed state")
73 }
74 path := "/npub10xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqpkge6d/private.git/info/refs?service=git-upload-pack"
75 req := httptest.NewRequest(http.MethodGet, "http://relay.test/r/main"+path, nil)
76 denied := httptest.NewRecorder()
77 app.ServeHTTP(denied, req)
78 if denied.Code != http.StatusUnauthorized || denied.Body.Len() != 0 {
79 t.Fatalf("unsigned private Git read status=%d body=%q", denied.Code, denied.Body.String())
80 }
81 req = httptest.NewRequest(http.MethodGet, "http://relay.test/r/main"+path, nil)
82 signRequest(t, req, "")
83 res := httptest.NewRecorder()
84 app.ServeHTTP(res, req)
85 if res.Code != http.StatusOK || !strings.Contains(res.Body.String(), "# service=git-upload-pack") {
86 t.Fatalf("authorized private Git read after restart status = %d, body=%q", res.Code, res.Body.String())
87 }
88 pushPath := "/npub10xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqpkge6d/private.git/git-receive-pack"
89 body := "0000"
90 push := httptest.NewRequest(http.MethodPost, "http://relay.test/r/main"+pushPath, strings.NewReader(body))
91 push.Header.Set("Content-Type", "application/x-git-receive-pack-request")
92 signRequest(t, push, body)
93 pushResult := httptest.NewRecorder()
94 app.ServeHTTP(pushResult, push)
95 if pushResult.Code != http.StatusOK || pushResult.Header().Get("Content-Type") != "application/x-git-receive-pack-result" {
96 t.Fatalf("signed private Git negotiation failed: %d %q", pushResult.Code, pushResult.Body.String())
97 }
98 tampered := httptest.NewRequest(http.MethodPost, "http://relay.test/r/main"+pushPath, strings.NewReader("0002"))
99 tampered.Header.Set("Content-Type", "application/x-git-receive-pack-request")
100 signRequest(t, tampered, "0001")
101 rejected := httptest.NewRecorder()
102 app.ServeHTTP(rejected, tampered)
103 if rejected.Code != http.StatusOK {
104 t.Fatalf("GRASP-08 reusable proof rejected body variation: %d %q", rejected.Code, rejected.Body.String())
105 }
106}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs