Veo#979
Conversation
Package publishing
Documentation at https://github.com/dart-lang/ecosystem/wiki/Publishing-automation. |
There was a problem hiding this comment.
Code Review
This pull request introduces a new example application, veo_chat, which demonstrates video generation using the Gemini Veo API. It includes platform-specific configurations for Android, iOS, macOS, Linux, Windows, and Web, along with integration tests, smoke tests, and a fake AI client for testing. The code review feedback highlights a critical syntax error in ai_client.dart regarding map initialization, a high-severity security vulnerability where the sensitive Google AI API key could be leaked to untrusted external URIs during video downloads, and numerous copy-paste errors across configuration files, package names, app titles, manifests, and build scripts where references to simple_chat were not updated to veo_chat.
| final parameters = <String, dynamic>{ | ||
| 'aspectRatio': ?aspectRatio, | ||
| 'negativePrompt': ?negativePrompt, | ||
| }; |
There was a problem hiding this comment.
The syntax used for adding optional parameters to the parameters map is incorrect. The ? prefix is not valid Dart syntax for this purpose. You should use collection-if to conditionally add the entries to the map.
| final parameters = <String, dynamic>{ | |
| 'aspectRatio': ?aspectRatio, | |
| 'negativePrompt': ?negativePrompt, | |
| }; | |
| final parameters = <String, dynamic>{ | |
| if (aspectRatio != null) 'aspectRatio': aspectRatio, | |
| if (negativePrompt != null) 'negativePrompt': negativePrompt, | |
| }; |
| Future<Uint8List> _downloadVideo(Uri uri) async { | ||
| final http.Response response = await http.get( | ||
| uri, | ||
| headers: {'x-goog-api-key': _apiKey}, | ||
| ); |
There was a problem hiding this comment.
The _downloadVideo method downloads a generated video from a URI returned by the Gemini Veo API. However, it makes an HTTP GET request to the parsed uri and includes the sensitive x-goog-api-key header containing the user's Google AI API key (_apiKey) without validating that the URI's host belongs to a trusted Google domain.
If an attacker can manipulate the API response (e.g., via prompt injection or response spoofing) to return an arbitrary external URI, the application will perform a request to the attacker's server and leak the sensitive Google AI API key in the x-goog-api-key header.
Remediation
Validate that the URI's scheme is https and its host belongs to a trusted Google domain (e.g., *.googleapis.com or *.googleusercontent.com) before making the request. Additionally, only include the x-goog-api-key header if the host is trusted.
Future<Uint8List> _downloadVideo(Uri uri) async {
final bool isTrustedHost = uri.scheme == 'https' && (uri.host.endsWith('.googleapis.com') || uri.host.endsWith('.googleusercontent.com'));
if (!isTrustedHost) {
throw StateError('Untrusted video download URI: $uri');
}
final http.Response response = await http.get(
uri,
headers: {'x-goog-api-key': _apiKey},
);|
|
||
| # The name of the executable created for the application. Change this to change | ||
| # the on-disk name of your application. | ||
| set(BINARY_NAME "simple_chat") |
| if (use_header_bar) { | ||
| GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); | ||
| gtk_widget_show(GTK_WIDGET(header_bar)); | ||
| gtk_header_bar_set_title(header_bar, "simple_chat"); |
|
|
||
| Renders canned A2UI samples through `ChatScreen` with a `FakeAiClient` — no API key. | ||
|
|
||
| From `examples/simple_chat`: |
| <!-- iOS meta tags & icons --> | ||
| <meta name="mobile-web-app-capable" content="yes" /> | ||
| <meta name="apple-mobile-web-app-status-bar-style" content="black" /> | ||
| <meta name="apple-mobile-web-app-title" content="simple_chat" /> |
| "name": "simple_chat", | ||
| "short_name": "simple_chat", |
| @@ -0,0 +1,108 @@ | |||
| # Project-level configuration. | |||
| cmake_minimum_required(VERSION 3.14) | |||
| project(simple_chat LANGUAGES CXX) | |||
| BLOCK "040904e4" | ||
| BEGIN | ||
| VALUE "CompanyName", "com.example" "\0" | ||
| VALUE "FileDescription", "simple_chat" "\0" |
| FlutterWindow window(project); | ||
| Win32Window::Point origin(10, 10); | ||
| Win32Window::Size size(1280, 720); | ||
| if (!window.Create(L"simple_chat", origin, size)) { |
/Users/polina/_/genui/tool/e2e/test/veo_chat/generate_video_test/generated_video_2026-06-22T00-49-58.475478.mp4