mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
## Description This PR replaces `wcslen` with `wcsnlen` in the Windows runner template and all example/dev/integration test files to address CWE-126 (Buffer Over-read) flagged by static analysis tools (Semgrep/GitLab SAST). ## Changes The `Utf8FromUtf16` function now uses `wcsnlen` with the `UNICODE_STRING_MAX_CHARS` constant (32767) as the maximum length, providing defensive programming against potential buffer over-reads. **Key improvements:** 1. Calculate `input_length` **first** using `wcsnlen(utf16_string, UNICODE_STRING_MAX_CHARS)` 2. Use that bounded length for **both** `WideCharToMultiByte` calls (eliminates the `-1` unbounded read) 3. Remove the `-1` adjustment since explicit length excludes null terminator 4. Use `static_cast` instead of C-style casts per Google C++ Style Guide ## Test Coverage Added comprehensive edge case tests for `Utf8FromUtf16` in `windows_startup_test`: - **nullptr input**: Verifies function returns empty string - **Empty string input**: Verifies function returns empty string - **Invalid UTF-16 (unpaired surrogate)**: Verifies function handles malformed input gracefully These tests address reviewer feedback from @loic-sharma requesting coverage for corner cases. ## Files Updated **Template (source of truth):** - `packages/flutter_tools/templates/app/windows.tmpl/runner/utils.cpp` **Integration tests (4 files):** - `dev/integration_tests/flutter_gallery/windows/runner/utils.cpp` - `dev/integration_tests/ui/windows/runner/utils.cpp` - `dev/integration_tests/windowing_test/windows/runner/utils.cpp` - `dev/integration_tests/windows_startup_test/windows/runner/utils.cpp` **Examples and dev apps (10 files):** - `examples/hello_world/windows/runner/utils.cpp` - `examples/layers/windows/runner/utils.cpp` - `examples/platform_view/windows/runner/utils.cpp` - `examples/flutter_view/windows/runner/utils.cpp` - `examples/platform_channel/windows/runner/utils.cpp` - `examples/api/windows/runner/utils.cpp` - `examples/multiple_windows/windows/runner/utils.cpp` - `dev/manual_tests/windows/runner/utils.cpp` - `dev/benchmarks/complex_layout/windows/runner/utils.cpp` - `dev/a11y_assessments/windows/runner/utils.cpp` **Test files (4 files):** - `dev/integration_tests/windows_startup_test/windows/runner/flutter_window.cpp` - `dev/integration_tests/windows_startup_test/lib/main.dart` - `dev/integration_tests/windows_startup_test/lib/windows.dart` - `dev/integration_tests/windows_startup_test/test_driver/main_test.dart` ## Rationale While the Windows API guarantees null-termination for strings returned by `CommandLineToArgvW`, using `wcsnlen` with an explicit bound is a defensive programming best practice that: - Satisfies static analysis tools - Provides an extra safety layer - Follows the principle of defense in depth The limit of 32767 (`UNICODE_STRING_MAX_CHARS`) is the maximum length of a `UNICODE_STRING` structure and is far beyond any realistic command-line argument length. ## Related Issues Fixes https://github.com/flutter/flutter/issues/180418 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and labeled this PR with `severe: API break` if it contains a breaking change. - [x] All existing and new tests are passing. [Contributor Guide]: https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/master/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/master/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [breaking change policy]: https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#breaking-changes
83 lines
2.4 KiB
Dart
83 lines
2.4 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
const MethodChannel _kMethodChannel = MethodChannel('tests.flutter.dev/windows_startup_test');
|
|
|
|
/// Returns true if the application's window is visible.
|
|
Future<bool> isWindowVisible() async {
|
|
final bool? visible = await _kMethodChannel.invokeMethod<bool?>('isWindowVisible');
|
|
if (visible == null) {
|
|
throw 'Method channel unavailable';
|
|
}
|
|
|
|
return visible;
|
|
}
|
|
|
|
/// Returns true if the app's dark mode is enabled.
|
|
Future<bool> isAppDarkModeEnabled() async {
|
|
final bool? enabled = await _kMethodChannel.invokeMethod<bool?>('isAppDarkModeEnabled');
|
|
if (enabled == null) {
|
|
throw 'Method channel unavailable';
|
|
}
|
|
|
|
return enabled;
|
|
}
|
|
|
|
/// Returns true if the operating system dark mode setting is enabled.
|
|
Future<bool> isSystemDarkModeEnabled() async {
|
|
final bool? enabled = await _kMethodChannel.invokeMethod<bool?>('isSystemDarkModeEnabled');
|
|
if (enabled == null) {
|
|
throw 'Method channel unavailable';
|
|
}
|
|
|
|
return enabled;
|
|
}
|
|
|
|
/// Test conversion of a UTF16 string to UTF8 using the app template utils.
|
|
Future<String> testStringConversion(Int32List twoByteCodes) async {
|
|
final String? converted = await _kMethodChannel.invokeMethod<String?>(
|
|
'convertString',
|
|
twoByteCodes,
|
|
);
|
|
if (converted == null) {
|
|
throw 'Method channel unavailable.';
|
|
}
|
|
|
|
return converted;
|
|
}
|
|
|
|
/// Test that Utf8FromUtf16 handles nullptr gracefully.
|
|
Future<String> testNullStringConversion() async {
|
|
final String? converted = await _kMethodChannel.invokeMethod<String?>('convertNullString');
|
|
if (converted == null) {
|
|
throw 'Method channel unavailable.';
|
|
}
|
|
|
|
return converted;
|
|
}
|
|
|
|
/// Test that Utf8FromUtf16 handles empty string gracefully.
|
|
Future<String> testEmptyStringConversion() async {
|
|
final String? converted = await _kMethodChannel.invokeMethod<String?>('convertEmptyString');
|
|
if (converted == null) {
|
|
throw 'Method channel unavailable.';
|
|
}
|
|
|
|
return converted;
|
|
}
|
|
|
|
/// Test that Utf8FromUtf16 handles invalid UTF-16 (unpaired surrogate) gracefully.
|
|
Future<String> testInvalidUtf16Conversion() async {
|
|
final String? converted = await _kMethodChannel.invokeMethod<String?>('convertInvalidUtf16');
|
|
if (converted == null) {
|
|
throw 'Method channel unavailable.';
|
|
}
|
|
|
|
return converted;
|
|
}
|