d247213bdd9d

refuse a steal address whose repo is not a name Kepr serves, before it reaches a URL or a folder (3.0.107)

time
2026-09-28 05:15 UTC · 2 weeks ago
machine
Junes-MacBook-Pro.local
agent
claude-opus-5-5
session
fix a2 review findings
parent
7ec030db3e06
4 files VERSION+1 −1src/cli/cmd_steal.v+9 −3tests/cli_test.v+3 −1v.mod+1 −1
VERSION+1 −1
@@ -1 +1 @@1-3.0.1061+3.0.107

view file

src/cli/cmd_steal.v+9 −3
@@ -385,15 +385,21 @@385385 386386 // web_address splits a `<host>/<repo>` argument out of a steal name: a slash makes it an address387387 // (`kepr.uk/nina`, `http://127.0.0.1:3917/shipped`), a bare name is this machine's own remote. An388-// addressed value that is missing either half is an error, never a name.388+// addressed value that is missing either half is an error, never a name. The repo half becomes a389+// URL path segment and a folder here, so it must be a name Kepr serves: letters, digits, `.`, `_`390+// and `-`, and never `.` or `..`.389391 fn web_address(value string) ![]string {390392 	if !value.contains('/') {391393 		return []string{}392394 	}393395 	repo := value.all_after_last('/')394396 	host := value.all_before_last('/')395-	if repo == '' || host == '' {396-		return error('"${value}" is not <host>/<repo>')397+	if host == '' || repo in ['', '.', '..'] || !repo.bytes().all(it.is_alnum() || it in [398+		`.`,399+		`_`,400+		`-`,401+	]) {402+		return error('"${value}" is not <host>/<repo> — a repo name is letters, digits, ., _ and -')397403 	}398404 	return [host, repo]399405 }

view file

tests/cli_test.v+3 −1
@@ -1487,7 +1487,9 @@14871487 	home := temp_repo('steal-address-home')14881488 	defer { os.rmdir_all(outside) or {} }14891489 	defer { os.rmdir_all(home) or {} }1490-	for bad in ['kepr.uk/', '/shipped', 'kepr.uk/'] {1490+	// The repo half becomes a URL path segment and a folder here, so it is a name Kepr serves.1491+	for bad in ['kepr.uk/', '/shipped', 'kepr.uk/..', 'kepr.uk/.', 'kepr.uk/a b', 'kepr.uk/nina?x',1492+		'kepr.uk/nina#x', 'kepr.uk/nína'] {14911493 		refused := run_home(outside, home, 'steal', bad)14921494 		assert refused.code == 1, bad14931495 		assert refused.err.contains('is not <host>/<repo>'), '${bad}: ${refused.err}'

view file

v.mod+1 −1
@@ -1,6 +1,6 @@11 Module {22 	name: 'koh3'3-	version: '3.0.106'3+	version: '3.0.107'44 	description: 'One SQLite file per repo, one line of history.'55 	license: 'MIT'66 }

view file