Skip to content

Commit ae0128d

Browse files
authored
Merge branch 'master' into cache-headers
2 parents 4831d20 + aa2ec87 commit ae0128d

File tree

13 files changed

+88
-24
lines changed

13 files changed

+88
-24
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ Important changes to data models, configuration, and migrations between each
22
AppEngine version, listed here to ease deployment and troubleshooting.
33

44
## Next Release (replace with git tag when deployed)
5-
* Bump runtimeVersion to `2025.10.21`.
5+
* Bump runtimeVersion to `2025.10.22`.
66
* Upgraded stable Flutter analysis SDK to `3.35.6`.
7+
* Upgraded pana to `0.23.0`.
78
* Added more explicitly public `cache-control` to content pages.
89

910
## `20251017t101000-all`

app/lib/fake/backend/fake_pana_runner.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ Future<Summary> fakePanaSummary({
9999
.where((url) => fakeUrlCheck('funding', url) != null)
100100
.toList(),
101101
contributingUrl: contributingUrl,
102-
licenses: [License(path: '', spdxIdentifier: licenseSpdx)],
102+
licenses: [License(spdxIdentifier: licenseSpdx)],
103103
);
104104
return Summary(
105105
createdAt: clock.now().toUtc(),
@@ -156,7 +156,6 @@ Future<Summary> fakePanaSummary({
156156
],
157157
),
158158
result: result,
159-
licenses: [],
160159
errorMessage: null,
161160
pubspec: null, // will be ignored
162161
);

app/lib/fake/backend/fake_pub_worker.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,8 +337,6 @@ Summary _emptySummary(String package, String version) {
337337
tags: null,
338338
report: null,
339339
result: null,
340-
licenseFile: null,
341-
licenses: null,
342340
errorMessage: null,
343341
pubspec: null, // will be ignored
344342
);

app/lib/frontend/templates/views/pkg/info_box.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ d.Node packageInfoBoxNode({
4343
if (data.versionInfo.hasLicense) {
4444
final licenses = data.scoreCard.panaReport?.licenses ?? <License>[];
4545
if (licenses.isEmpty) {
46-
licenses.add(License(path: 'LICENSE', spdxIdentifier: 'unknown'));
46+
licenses.add(License(spdxIdentifier: 'unknown'));
4747
}
4848
license = _licenseNode(
4949
licenses: licenses,

app/lib/scorecard/models.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ class PanaReport {
140140
}.toList(),
141141
allDependencies: summary?.allDependencies,
142142
// ignore: deprecated_member_use
143-
licenses: summary?.result?.licenses ?? summary?.licenses,
143+
licenses: summary?.result?.licenses,
144144
report: summary?.report,
145145
result: summary?.result,
146146
urlProblems: summary?.urlProblems,

app/lib/shared/versions.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ final RegExp runtimeVersionPattern = RegExp(r'^\d{4}\.\d{2}\.\d{2}$');
2424
/// when the version switch happens.
2525
const _acceptedRuntimeVersions = <String>[
2626
// The current [runtimeVersion].
27-
'2025.10.21',
27+
'2025.10.22',
2828
// Fallback runtime versions.
2929
'2025.10.17',
3030
'2025.10.10',

app/lib/tool/test_profile/resolver.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,11 @@ Future<List<ResolvedVersion>> resolveVersions(
6464
),
6565
);
6666

67-
final pr = await toolEnv.runUpgrade(dummyDir.path, false);
67+
final pr = await toolEnv.runPub(
68+
dummyDir.path,
69+
command: 'upgrade',
70+
usesFlutter: false,
71+
);
6872
if (pr.exitCode != 0) {
6973
throw Exception(
7074
'dart pub get on `${package.name} $version` exited with ${pr.exitCode}.\n${pr.stderr}',

app/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ dependencies:
4646
watcher: ^1.0.0
4747
yaml: ^3.1.0
4848
# pana version to be pinned
49-
pana: '0.22.24'
49+
pana: '0.23.0'
5050
# 3rd-party packages with pinned versions
5151
mailer: '6.5.0'
5252
postgres: '3.5.8'

app/test/dartdoc/dartdoc_page_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ void main() {
7070
dartdocVersion: dartdocVersion,
7171
pubCacheDir: pubCacheDir,
7272
);
73-
await toolEnv.runUpgrade(pkgDir, false);
73+
await toolEnv.runPub(pkgDir, command: 'get', usesFlutter: false);
7474
});
7575

7676
tearDownAll(() async {

pkg/image_proxy/lib/image_proxy_service.dart

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ Duration timeoutDelay = Duration(seconds: isTesting ? 1 : 8);
2323
/// The keys we currently allow the url to be signed with.
2424
Map<int, Uint8List> allowedKeys = {};
2525

26+
// Inspired by https://github.com/atmos/camo/blob/master/server.coffee#L39.
27+
Map<String, String> securityHeaders = {
28+
'X-Frame-Options': 'deny',
29+
'X-XSS-Protection': '1; mode=block',
30+
'X-Content-Type-Options': 'nosniff',
31+
'Content-Security-Policy':
32+
"default-src 'none'; img-src data:; style-src 'unsafe-inline'",
33+
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
34+
};
35+
2636
/// Ensure that [allowedKeys] contains keys for today and the two surrounding
2737
/// days.
2838
Future<void> updateAllowedKeys() async {
@@ -104,13 +114,17 @@ final maxImageSize = 1024 * 1024 * 10; // At most 10 MB.
104114
Future<shelf.Response> handler(shelf.Request request) async {
105115
try {
106116
if (request.method != 'GET') {
107-
return shelf.Response.notFound('Unsupported method');
117+
return shelf.Response.notFound(
118+
'Unsupported method',
119+
headers: securityHeaders,
120+
);
108121
}
109122
final segments = request.url.pathSegments;
110123
if (segments.length != 3) {
111124
return shelf.Response.badRequest(
112125
body:
113126
'malformed request, ${segments.length} should be of the form <base64(hmac(url,daily_secret))>/<date>/<urlencode(url)>',
127+
headers: securityHeaders,
114128
);
115129
}
116130
final Uint8List signature;
@@ -119,22 +133,30 @@ Future<shelf.Response> handler(shelf.Request request) async {
119133
} on FormatException catch (_) {
120134
return shelf.Response.badRequest(
121135
body: 'malformed request, could not decode mac signature',
136+
headers: securityHeaders,
122137
);
123138
}
124139
final date = int.tryParse(segments[1]);
125140
if (date == null) {
126-
return shelf.Response.badRequest(body: 'malformed request, missing date');
141+
return shelf.Response.badRequest(
142+
body: 'malformed request, missing date',
143+
headers: securityHeaders,
144+
);
127145
}
128146
final secret = allowedKeys[date];
129147
if (secret == null) {
130148
return shelf.Response.badRequest(
131149
body: 'malformed request, proxy url expired',
150+
headers: securityHeaders,
132151
);
133152
}
134153

135154
final imageUrl = segments[2];
136155
if (imageUrl.length > 1024) {
137-
return shelf.Response.badRequest(body: 'proxied url too long');
156+
return shelf.Response.badRequest(
157+
body: 'proxied url too long',
158+
headers: securityHeaders,
159+
);
138160
}
139161
final imageUrlBytes = utf8.encode(imageUrl);
140162

@@ -143,16 +165,23 @@ Future<shelf.Response> handler(shelf.Request request) async {
143165
try {
144166
parsedImageUrl = Uri.parse(imageUrl);
145167
} on FormatException catch (e) {
146-
return shelf.Response.badRequest(body: 'Malformed proxied url $e');
168+
return shelf.Response.badRequest(
169+
body: 'Malformed proxied url $e',
170+
headers: securityHeaders,
171+
);
147172
}
148173
if (!(parsedImageUrl.isScheme('http') ||
149174
parsedImageUrl.isScheme('https'))) {
150175
return shelf.Response.badRequest(
151176
body: 'Can only proxy http and https urls',
177+
headers: securityHeaders,
152178
);
153179
}
154180
if (!parsedImageUrl.isAbsolute) {
155-
return shelf.Response.badRequest(body: 'Can only proxy absolute urls');
181+
return shelf.Response.badRequest(
182+
body: 'Can only proxy absolute urls',
183+
headers: securityHeaders,
184+
);
156185
}
157186

158187
int statusCode;
@@ -233,14 +262,24 @@ Future<shelf.Response> handler(shelf.Request request) async {
233262
e is ServerSideException,
234263
);
235264
} on TooLargeException {
236-
return shelf.Response.badRequest(body: 'Image too large');
265+
return shelf.Response.badRequest(
266+
body: 'Image too large',
267+
headers: securityHeaders,
268+
);
237269
} on RedirectException catch (e) {
238-
return shelf.Response.badRequest(body: e.message);
270+
return shelf.Response.badRequest(
271+
body: e.message,
272+
headers: securityHeaders,
273+
);
239274
} on RequestTimeoutException catch (e) {
240-
return shelf.Response.badRequest(body: e.message);
275+
return shelf.Response.badRequest(
276+
body: e.message,
277+
headers: securityHeaders,
278+
);
241279
} on ServerSideException catch (e) {
242280
return shelf.Response.badRequest(
243281
body: 'Failed to retrieve image. Status code ${e.statusCode}',
282+
headers: securityHeaders,
244283
);
245284
}
246285

@@ -251,10 +290,11 @@ Future<shelf.Response> handler(shelf.Request request) async {
251290
'Cache-control': 'max-age=180, public',
252291
'content-type': ?contentType,
253292
'content-encoding': ?contentEncoding,
293+
...securityHeaders,
254294
},
255295
);
256296
} else {
257-
return shelf.Response.unauthorized('Bad hmac');
297+
return shelf.Response.unauthorized('Bad hmac', headers: securityHeaders);
258298
}
259299
} catch (e, st) {
260300
stderr.writeln('Uncaught error: $e $st');

0 commit comments

Comments
 (0)