@@ -527,6 +527,7 @@ class PackageBackend {
527
527
final repository = githubConfig.repository? .trim () ?? '' ;
528
528
githubConfig.repository = repository.isEmpty ? null : repository;
529
529
final tagPattern = githubConfig.tagPattern? .trim () ?? '' ;
530
+ verifyTagPattern (tagPattern: tagPattern);
530
531
githubConfig.tagPattern = tagPattern.isEmpty ? null : tagPattern;
531
532
final environment = githubConfig.environment? .trim () ?? '' ;
532
533
githubConfig.environment = environment.isEmpty ? null : environment;
@@ -544,15 +545,6 @@ class PackageBackend {
544
545
'The `repository` field has invalid characters.' );
545
546
}
546
547
547
- final tagPatternParts = tagPattern.split ('{{version}}' );
548
- InvalidInputException .check (tagPatternParts.length == 2 ,
549
- 'The `tagPattern` field must contain a single `{{version}}` part.' );
550
- InvalidInputException .check (
551
- tagPatternParts
552
- .where ((e) => e.isNotEmpty)
553
- .every (_validGitHubVersionPattern.hasMatch),
554
- 'The `tagPattern` field has invalid characters.' );
555
-
556
548
InvalidInputException .check (
557
549
! githubConfig.requireEnvironment || environment.isNotEmpty,
558
550
'The `environment` field must not be empty when enabled.' );
@@ -1339,11 +1331,6 @@ class PackageBackend {
1339
1331
if (repository == null || repository.isEmpty) {
1340
1332
throw AssertionError ('Missing or empty repository.' );
1341
1333
}
1342
- final tagPattern = githubConfig.tagPattern ?? '' ;
1343
- if (! tagPattern.contains ('{{version}}' )) {
1344
- throw AssertionError (
1345
- 'Configured tag pattern does not include `{{version}}`' );
1346
- }
1347
1334
final requireEnvironment = githubConfig.requireEnvironment;
1348
1335
final environment = githubConfig.environment;
1349
1336
if (requireEnvironment && (environment == null || environment.isEmpty)) {
@@ -1375,25 +1362,11 @@ class PackageBackend {
1375
1362
throw AuthorizationException .githubActionIssue (
1376
1363
'publishing is only allowed from "tag" refType, this token has "${agent .payload .refType }" refType' );
1377
1364
}
1378
- final expectedRefStart = 'refs/tags/' ;
1379
- if (! agent.payload.ref.startsWith (expectedRefStart)) {
1380
- throw AuthorizationException .githubActionIssue (
1381
- 'publishing is only allowed from "refs/tags/*" ref, this token has "${agent .payload .ref }" ref' );
1382
- }
1383
- final expectedTagValue = tagPattern.replaceFirst ('{{version}}' , newVersion);
1384
- if (agent.payload.ref != 'refs/tags/$expectedTagValue ' ) {
1385
- // At this point we have concluded that the agent has push rights to the repository,
1386
- // however, the tag pattern they have used is not the one we expect.
1387
- //
1388
- // By revealing the expected tag pattern, we are serving the users with better
1389
- // error message, while not exposing much information to an assumed attacker.
1390
- // With the current access level, an attacker would have access to past tags, and
1391
- // figuring out the tag pattern from those should be straightforward anyway.
1392
- throw AuthorizationException .githubActionIssue (
1393
- 'publishing is configured to only be allowed from actions with specific ref pattern, '
1394
- 'this token has "${agent .payload .ref }" ref for which publishing is not allowed. '
1395
- 'Expected tag "$expectedTagValue ". Check that the version in the tag matches the version in "pubspec.yaml"' );
1396
- }
1365
+ verifyTagPatternWithRef (
1366
+ tagPattern: githubConfig.tagPattern ?? '' ,
1367
+ ref: agent.payload.ref,
1368
+ newVersion: newVersion,
1369
+ );
1397
1370
1398
1371
// When environment is configured, it must match the action's environment.
1399
1372
if (requireEnvironment && environment != agent.payload.environment) {
@@ -1759,6 +1732,54 @@ Future<void> purgePackageCache(String package) async {
1759
1732
]);
1760
1733
}
1761
1734
1735
+ /// Verifies the [tagPattern] before storing it on the automated publishing
1736
+ /// settings object.
1737
+ @visibleForTesting
1738
+ void verifyTagPattern ({required String tagPattern}) {
1739
+ final tagPatternParts = tagPattern.split ('{{version}}' );
1740
+ InvalidInputException .check (tagPatternParts.length == 2 ,
1741
+ 'The `tagPattern` field must contain a single `{{version}}` part.' );
1742
+ InvalidInputException .check (
1743
+ tagPatternParts
1744
+ .where ((e) => e.isNotEmpty)
1745
+ .every (_validGitHubVersionPattern.hasMatch),
1746
+ 'The `tagPattern` field has invalid characters.' );
1747
+ }
1748
+
1749
+ /// Verifies the user-settings [tagPattern] with the authentication-provided
1750
+ /// [ref] value, and throws if the ref is not allowed or not recognized as
1751
+ /// valid pattern.
1752
+ @visibleForTesting
1753
+ void verifyTagPatternWithRef ({
1754
+ required String tagPattern,
1755
+ required String ref,
1756
+ required String newVersion,
1757
+ }) {
1758
+ if (! tagPattern.contains ('{{version}}' )) {
1759
+ throw AssertionError (
1760
+ 'Configured tag pattern does not include `{{version}}`' );
1761
+ }
1762
+ final expectedRefStart = 'refs/tags/' ;
1763
+ if (! ref.startsWith (expectedRefStart)) {
1764
+ throw AuthorizationException .githubActionIssue (
1765
+ 'publishing is only allowed from "refs/tags/*" ref, this token has "$ref " ref' );
1766
+ }
1767
+ final expectedTagValue = tagPattern.replaceFirst ('{{version}}' , newVersion);
1768
+ if (ref != 'refs/tags/$expectedTagValue ' ) {
1769
+ // At this point we have concluded that the agent has push rights to the repository,
1770
+ // however, the tag pattern they have used is not the one we expect.
1771
+ //
1772
+ // By revealing the expected tag pattern, we are serving the users with better
1773
+ // error message, while not exposing much information to an assumed attacker.
1774
+ // With the current access level, an attacker would have access to past tags, and
1775
+ // figuring out the tag pattern from those should be straightforward anyway.
1776
+ throw AuthorizationException .githubActionIssue (
1777
+ 'publishing is configured to only be allowed from actions with specific ref pattern, '
1778
+ 'this token has "$ref " ref for which publishing is not allowed. '
1779
+ 'Expected tag "$expectedTagValue ". Check that the version in the tag matches the version in "pubspec.yaml"' );
1780
+ }
1781
+ }
1782
+
1762
1783
/// The status of an invite after being created or updated.
1763
1784
class InviteStatus {
1764
1785
final String ? urlNonce;
0 commit comments