Signed-off-by: oppiliappan me@oppi.li
+59
-21
Diff
round #1
+1
knotserver/config/config.go
+1
knotserver/config/config.go
···
23
23
JetstreamEndpoint string `env:"JETSTREAM_ENDPOINT, default=wss://jetstream1.us-west.bsky.network/subscribe"`
24
24
Owner string `env:"OWNER, required"`
25
25
LogDids bool `env:"LOG_DIDS, default=true"`
26
+
MaxResponseKB int `env:"MAX_RESPONSE_KB, default=5120"`
26
27
27
28
// This disables signature verification so use with caution.
28
29
Dev bool `env:"DEV, default=false"`
+2
-2
knotserver/xrpc/create_repo.go
+2
-2
knotserver/xrpc/create_repo.go
···
109
109
if _, statErr := os.Stat(didRepoPath); statErr == nil {
110
110
l.Info("repo already exists from previous attempt", "repoDid", existingDid)
111
111
output := tangled.RepoCreate_Output{RepoDid: &existingDid}
112
-
writeJson(w, &output)
112
+
h.writeJson(w, &output)
113
113
return
114
114
}
115
115
l.Warn("stale repo key found without directory, cleaning up", "repoDid", existingDid)
···
250
250
}
251
251
}()
252
252
253
-
writeJson(w, &tangled.RepoCreate_Output{RepoDid: &repoDid})
253
+
h.writeJson(w, &tangled.RepoCreate_Output{RepoDid: &repoDid})
254
254
}
255
255
256
256
func (h *Xrpc) requestCrawl(ctx context.Context, input *tangled.SyncRequestCrawl_Input) error {
+1
-1
knotserver/xrpc/list_keys.go
+1
-1
knotserver/xrpc/list_keys.go
+1
-1
knotserver/xrpc/owner.go
+1
-1
knotserver/xrpc/owner.go
+2
-2
knotserver/xrpc/repo_blob.go
+2
-2
knotserver/xrpc/repo_blob.go
···
58
58
Branch: &submodule.Branch,
59
59
},
60
60
}
61
-
writeJson(w, response)
61
+
x.writeJson(w, response)
62
62
return
63
63
}
64
64
···
173
173
}
174
174
}
175
175
176
-
writeJson(w, response)
176
+
x.writeJson(w, response)
177
177
}
178
178
179
179
// isTextualMimeType returns true if the MIME type represents textual content
+1
-1
knotserver/xrpc/repo_branch.go
+1
-1
knotserver/xrpc/repo_branch.go
+1
-1
knotserver/xrpc/repo_branches.go
+1
-1
knotserver/xrpc/repo_branches.go
+1
-1
knotserver/xrpc/repo_compare.go
+1
-1
knotserver/xrpc/repo_compare.go
+1
-1
knotserver/xrpc/repo_diff.go
+1
-1
knotserver/xrpc/repo_diff.go
+1
-1
knotserver/xrpc/repo_get_default_branch.go
+1
-1
knotserver/xrpc/repo_get_default_branch.go
+1
-1
knotserver/xrpc/repo_languages.go
+1
-1
knotserver/xrpc/repo_languages.go
+1
-1
knotserver/xrpc/repo_log.go
+1
-1
knotserver/xrpc/repo_log.go
+1
-1
knotserver/xrpc/repo_tag.go
+1
-1
knotserver/xrpc/repo_tag.go
+1
-1
knotserver/xrpc/repo_tree.go
+1
-1
knotserver/xrpc/repo_tree.go
+1
-1
knotserver/xrpc/version.go
+1
-1
knotserver/xrpc/version.go
+29
-4
knotserver/xrpc/xrpc.go
+29
-4
knotserver/xrpc/xrpc.go
···
1
1
package xrpc
2
2
3
3
import (
4
+
"bytes"
4
5
"encoding/json"
6
+
"errors"
5
7
"log/slog"
6
8
"net/http"
7
9
"os"
···
122
124
json.NewEncoder(w).Encode(e)
123
125
}
124
126
125
-
func writeJson(w http.ResponseWriter, response any) {
126
-
w.Header().Set("Content-Type", "application/json")
127
-
if err := json.NewEncoder(w).Encode(response); err != nil {
128
-
writeError(w, xrpcerr.GenericError(err), http.StatusInternalServerError)
127
+
type limitWriter struct {
128
+
buf bytes.Buffer
129
+
limit int
130
+
written int
131
+
}
132
+
133
+
var errResponseTooLarge = errors.New("response too large")
134
+
135
+
func (lw *limitWriter) Write(p []byte) (int, error) {
136
+
if lw.written+len(p) > lw.limit {
137
+
return 0, errResponseTooLarge
138
+
}
139
+
n, err := lw.buf.Write(p)
140
+
lw.written += n
141
+
return n, err
142
+
}
143
+
144
+
func (x *Xrpc) writeJson(w http.ResponseWriter, response any) {
145
+
lw := &limitWriter{limit: x.Config.Server.MaxResponseKB * 1024}
146
+
if err := json.NewEncoder(lw).Encode(response); err != nil {
147
+
if errors.Is(err, errResponseTooLarge) {
148
+
writeError(w, xrpcerr.RequestTooLargeError, http.StatusRequestEntityTooLarge)
149
+
} else {
150
+
writeError(w, xrpcerr.GenericError(err), http.StatusInternalServerError)
151
+
}
129
152
return
130
153
}
154
+
w.Header().Set("Content-Type", "application/json")
155
+
w.Write(lw.buf.Bytes())
131
156
}
+7
nix/modules/knot.nix
+7
nix/modules/knot.nix
···
177
177
default = false;
178
178
description = "Enable development mode (disables signature verification)";
179
179
};
180
+
181
+
maxResponseKB = mkOption {
182
+
type = types.int;
183
+
default = 5120;
184
+
description = "Maximum response size in kilobytes";
185
+
};
180
186
};
181
187
};
182
188
};
···
282
288
then "true"
283
289
else "false"
284
290
}"
291
+
"KNOT_SERVER_MAX_RESPONSE_KB=${toString cfg.server.maxResponseKB}"
285
292
];
286
293
ExecStart = "${cfg.package}/bin/knot server";
287
294
Restart = "always";
+5
xrpc/errors/errors.go
+5
xrpc/errors/errors.go
History
2 rounds
0 comments
1 commit
expand
collapse
knotserver: limit request size
Signed-off-by: oppiliappan <me@oppi.li>
merge conflicts detected
expand
collapse
expand
collapse
- knotserver/config/config.go:23
- knotserver/xrpc/create_repo.go:109
- knotserver/xrpc/list_keys.go:45
- knotserver/xrpc/owner.go:18
- knotserver/xrpc/repo_blob.go:58
- knotserver/xrpc/repo_branch.go:81
- knotserver/xrpc/repo_branches.go:45
- knotserver/xrpc/repo_compare.go:99
- knotserver/xrpc/repo_diff.go:37
- knotserver/xrpc/repo_get_default_branch.go:35
- knotserver/xrpc/repo_languages.go:72
- knotserver/xrpc/repo_log.go:82
- knotserver/xrpc/repo_tag.go:81
- knotserver/xrpc/repo_tags.go:75
- knotserver/xrpc/repo_tree.go:142
- knotserver/xrpc/version.go:56
- knotserver/xrpc/xrpc.go:1
- nix/modules/knot.nix:177
- xrpc/errors/errors.go:66