harden IPC: reject -prefixed git refs, cap files.read / git.log (T-104)
test / unit + widget + golden + a11y (push) Failing after 31s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m0s
test / unit + widget + golden + a11y (push) Failing after 31s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m0s
Three security fixes the consultant flagged: * git.checkout, git.push now reject branch/remote arguments starting with `-` via a top-level validateGitRef helper. `git push` also gets a `--` option terminator; checkout can't use `--` without changing semantics (it would be parsed as a pathspec), so the validator is the only line of defence there. * files.read caps responses at 10 MB so a single call can't OOM the UI on a multi-gigabyte log. * git.log caps `count` at 1000; git.diff / git.stage cap paths at 256. Excess is a userError rather than burning subprocess time. The bigger typed-schema framework (item 1 in T-104) is split out as T-120 since it needs design discussion alongside T-99. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -294,4 +294,45 @@ void main() {
|
||||
final r = await call('git.stage', {'paths': 'new.txt'});
|
||||
expect(r.ok, isTrue);
|
||||
});
|
||||
|
||||
test('git.checkout rejects a -prefixed branch (argv-injection guard)', () async {
|
||||
final r = await call('git.checkout', {'branch': '--upload-pack=evil'});
|
||||
expect(r.ok, isFalse);
|
||||
expect(r.error?.message, contains('branch'));
|
||||
});
|
||||
|
||||
test('git.push rejects a -prefixed remote', () async {
|
||||
final r = await call('git.push', {'remote': '--upload-pack=evil', 'branch': 'main'});
|
||||
expect(r.ok, isFalse);
|
||||
expect(r.error?.message, contains('remote'));
|
||||
});
|
||||
|
||||
test('git.push rejects a -prefixed branch', () async {
|
||||
final r = await call('git.push', {'remote': 'origin', 'branch': '--exec=evil'});
|
||||
expect(r.ok, isFalse);
|
||||
expect(r.error?.message, contains('branch'));
|
||||
});
|
||||
|
||||
test('git.log over the count cap fails as userError', () async {
|
||||
final r = await call('git.log', {'count': 100000});
|
||||
expect(r.ok, isFalse);
|
||||
expect(r.error?.kind, IpcErrorKind.userError);
|
||||
expect(r.error?.message, contains('exceeds cap'));
|
||||
});
|
||||
|
||||
test('git.diff with too many paths fails as userError', () async {
|
||||
final paths = [for (var i = 0; i < 300; i++) 'file_$i.txt'];
|
||||
final r = await call('git.diff', {'paths': paths});
|
||||
expect(r.ok, isFalse);
|
||||
expect(r.error?.kind, IpcErrorKind.userError);
|
||||
expect(r.error?.message, contains('exceeds cap'));
|
||||
});
|
||||
|
||||
test('git.stage with too many paths fails as userError', () async {
|
||||
final paths = [for (var i = 0; i < 300; i++) 'file_$i.txt'];
|
||||
final r = await call('git.stage', {'paths': paths});
|
||||
expect(r.ok, isFalse);
|
||||
expect(r.error?.kind, IpcErrorKind.userError);
|
||||
expect(r.error?.message, contains('exceeds cap'));
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user