-
-
Notifications
You must be signed in to change notification settings - Fork 81
Expand file tree
/
Copy pathsecurity_test.v
More file actions
98 lines (85 loc) · 3.93 KB
/
Copy pathsecurity_test.v
File metadata and controls
98 lines (85 loc) · 3.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
module main
import crypto.sha256
// Regression tests for the input validation that guards git command
// construction in create_file_in_bare_repo (see repo/file_routes.v). These
// inputs used to be interpolated into shell strings; they are now passed to
// git as plain arguments, but we still reject values git could misread as
// flags/refs or that contain control characters.
fn test_is_valid_repo_file_path_accepts_normal_paths() {
assert is_valid_repo_file_path('README.md')
assert is_valid_repo_file_path('src/main.v')
assert is_valid_repo_file_path('dir/file,with,commas.txt')
assert is_valid_repo_file_path('a/b/c/d.e')
}
fn test_is_valid_repo_file_path_rejects_dangerous_paths() {
assert !is_valid_repo_file_path('')
assert !is_valid_repo_file_path('/etc/passwd') // absolute
assert !is_valid_repo_file_path('-rf') // looks like a flag
assert !is_valid_repo_file_path('../../etc/passwd') // traversal
assert !is_valid_repo_file_path('a/../../b') // traversal
assert !is_valid_repo_file_path('file\nname') // newline
assert !is_valid_repo_file_path('file\x00name') // NUL
assert !is_valid_repo_file_path('a\tb') // tab / control char
}
fn test_is_safe_ref_accepts_normal_branches() {
assert is_safe_ref('master')
assert is_safe_ref('feature/new-thing')
assert is_safe_ref('release-1.2.3')
}
fn test_is_safe_ref_rejects_injection_attempts() {
assert !is_safe_ref('')
assert !is_safe_ref('--upload-pack=touch /tmp/pwned') // leading dash + space
assert !is_safe_ref('master;rm -rf /') // shell metacharacters
assert !is_safe_ref('master$(whoami)')
assert !is_safe_ref('master`id`')
assert !is_safe_ref('a..b') // ref traversal
assert !is_safe_ref('branch with spaces')
}
// Webhook SSRF guard: the IP classifiers must reject internal destinations and
// allow public ones. (is_safe_webhook_url itself does DNS and isn't unit-tested.)
fn test_is_blocked_ipv4_blocks_internal_ranges() {
assert is_blocked_ipv4('127.0.0.1') // loopback
assert is_blocked_ipv4('10.1.2.3') // private
assert is_blocked_ipv4('172.16.5.5') // private
assert is_blocked_ipv4('172.31.255.255') // private (edge)
assert is_blocked_ipv4('192.168.0.1') // private
assert is_blocked_ipv4('169.254.169.254') // link-local / cloud metadata
assert is_blocked_ipv4('0.0.0.0') // unspecified
assert is_blocked_ipv4('100.64.0.1') // CGNAT
assert is_blocked_ipv4('224.0.0.1') // multicast
assert is_blocked_ipv4('garbage') // unparseable -> fail closed
}
fn test_is_blocked_ipv4_allows_public() {
assert !is_blocked_ipv4('8.8.8.8')
assert !is_blocked_ipv4('1.1.1.1')
assert !is_blocked_ipv4('172.32.0.1') // just outside 172.16/12
assert !is_blocked_ipv4('172.15.0.1') // just outside 172.16/12
assert !is_blocked_ipv4('93.184.216.34')
}
fn test_is_blocked_ipv6() {
assert is_blocked_ipv6('::1') // loopback
assert is_blocked_ipv6('::') // unspecified
assert is_blocked_ipv6('fe80::1') // link-local
assert is_blocked_ipv6('fc00::1') // unique-local
assert is_blocked_ipv6('fd12:3456::1') // unique-local
assert is_blocked_ipv6('::ffff:127.0.0.1') // IPv4-mapped loopback
assert !is_blocked_ipv6('2606:4700:4700::1111') // public
assert !is_blocked_ipv6('::ffff:8.8.8.8') // IPv4-mapped public
}
// Password hashing: new hashes must be bcrypt, and legacy salted-SHA-256 hashes
// must still verify (so existing users aren't locked out before re-login).
fn test_new_passwords_are_bcrypt() {
h := hash_password_with_salt('s3cret-pw', 'ignored-salt')
assert h.starts_with('$2') // bcrypt hash
assert !password_hash_is_legacy(h)
assert compare_password_with_hash('s3cret-pw', 'ignored-salt', h)
assert !compare_password_with_hash('wrong-pw', 'ignored-salt', h)
}
fn test_legacy_sha256_hashes_still_verify() {
salt := 'abc123'
// Legacy scheme was sha256('${password}${salt}').
legacy := sha256.sum('hunter2${salt}'.bytes()).hex()
assert password_hash_is_legacy(legacy)
assert compare_password_with_hash('hunter2', salt, legacy)
assert !compare_password_with_hash('nope', salt, legacy)
}