vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Budget only the untrusted write role
e5542ac6b64fa3ebaf62af4831e55996dba45e7e · Keenan Nemetz · 13d ago · parent 9e8cc155
Budget only the untrusted write role Maintainers and admins are trusted roles: they can already change the rules, push shared branches and run the instance, so a write budget would slow their work without bounding anybody who could not do worse. The budget now applies only below maintain, which is the contribute role and the read-only accounts that cannot write at all. The setting is named for what it does: contributor_writes_per_hour. The role is the effective one on the repository, so an instance-wide maintainer on a public repository is exempt for the same reason a repository maintainer is.
server/web/auth_hardening_test.go +24−6
| @@ -14,6 +14,7 @@ import ( | |||
| 14 | 14 | "testing" | |
| 15 | 15 | "time" | |
| 16 | 16 | ||
| 17 | "github.com/rodent-software/vole/forge/access" | ||
| 17 | 18 | "github.com/rodent-software/vole/forge/config" | |
| 18 | 19 | "github.com/rodent-software/vole/forge/identity" | |
| 19 | 20 | ) | |
| @@ -279,17 +280,16 @@ func TestInviteTokensAreRedactedFromLogs(t *testing.T) { | |||
| 279 | 280 | } | |
| 280 | 281 | } | |
| 281 | 282 | ||
| 282 | // TestAccountWritesAreBudgeted: one account's browser writes are budgeted per hour, | ||
| 283 | // and the budget comes from the instance config. A person discussing never reaches | ||
| 284 | // it; a script posting in a loop does. | ||
| 285 | func TestAccountWritesAreBudgeted(t *testing.T) { | ||
| 283 | // TestContributorWritesAreBudgeted: an untrusted account's browser writes are | ||
| 284 | // budgeted per hour, and the budget comes from the instance config. | ||
| 285 | func TestContributorWritesAreBudgeted(t *testing.T) { | ||
| 286 | 286 | t.Parallel() | |
| 287 | 287 | limits := newAuthLimits(2) | |
| 288 | 288 | if limits.write.limit != 2 { | |
| 289 | 289 | t.Fatalf("write limit = %d, want 2", limits.write.limit) | |
| 290 | 290 | } | |
| 291 | if got := newAuthLimits(0).write.limit; got != defaultWritesPerHour { | ||
| 292 | t.Fatalf("default write limit = %d, want %d", got, defaultWritesPerHour) | ||
| 291 | if got := newAuthLimits(0).write.limit; got != defaultContributorWritesPerHour { | ||
| 292 | t.Fatalf("default write limit = %d, want %d", got, defaultContributorWritesPerHour) | ||
| 293 | 293 | } | |
| 294 | 294 | w := &Web{limits: limits} | |
| 295 | 295 | req := httptest.NewRequest("POST", "/r/demo/issues", nil) | |
| @@ -310,3 +310,21 @@ func TestAccountWritesAreBudgeted(t *testing.T) { | |||
| 310 | 310 | t.Fatal("one account's budget limited another") | |
| 311 | 311 | } | |
| 312 | 312 | } | |
| 313 | |||
| 314 | // TestOnlyUntrustedWritesAreBudgeted: maintainers and admins are trusted roles in | ||
| 315 | // the instance's threat model — they can change rules, push shared branches and run | ||
| 316 | // the instance — so the contributor budget does not apply to them. | ||
| 317 | func TestOnlyUntrustedWritesAreBudgeted(t *testing.T) { | ||
| 318 | t.Parallel() | ||
| 319 | for role, want := range map[access.Role]bool{ | ||
| 320 | access.None: true, | ||
| 321 | access.Read: true, | ||
| 322 | access.Contribute: true, | ||
| 323 | access.Maintain: false, | ||
| 324 | access.Admin: false, | ||
| 325 | } { | ||
| 326 | if got := writeBudgeted(role); got != want { | ||
| 327 | t.Fatalf("writeBudgeted(%s) = %v, want %v", role, got, want) | ||
| 328 | } | ||
| 329 | } | ||
| 330 | } | ||