diff --git a/src/Sdk/WorkflowParser/Conversion/WorkflowTemplateConverter.cs b/src/Sdk/WorkflowParser/Conversion/WorkflowTemplateConverter.cs index 0c53f87fc..d281a8ba9 100644 --- a/src/Sdk/WorkflowParser/Conversion/WorkflowTemplateConverter.cs +++ b/src/Sdk/WorkflowParser/Conversion/WorkflowTemplateConverter.cs @@ -1957,6 +1957,24 @@ namespace GitHub.Actions.WorkflowParser.Conversion context.Error(key, $"The permission 'models' is not allowed"); } break; + case "workflows": + if (context.GetFeatures().AllowWorkflowsPermission) + { + // Workflows only supports write; downgrade read to none + if (permissionLevel == PermissionLevel.Read) + { + permissions.Workflows = PermissionLevel.NoAccess; + } + else + { + permissions.Workflows = permissionLevel; + } + } + else + { + context.Error(key, $"The permission 'workflows' is not allowed"); + } + break; default: break; } diff --git a/src/Sdk/WorkflowParser/Permissions.cs b/src/Sdk/WorkflowParser/Permissions.cs index 0a211e5b0..eacac7a5b 100644 --- a/src/Sdk/WorkflowParser/Permissions.cs +++ b/src/Sdk/WorkflowParser/Permissions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Runtime.Serialization; using GitHub.Actions.WorkflowParser.Conversion; @@ -17,7 +17,7 @@ namespace GitHub.Actions.WorkflowParser public Permissions(Permissions copy) { Actions = copy.Actions; - ArtifactMetadata = copy.ArtifactMetadata; + ArtifactMetadata = copy.ArtifactMetadata; Attestations = copy.Attestations; Checks = copy.Checks; Contents = copy.Contents; @@ -32,6 +32,7 @@ namespace GitHub.Actions.WorkflowParser SecurityEvents = copy.SecurityEvents; IdToken = copy.IdToken; Models = copy.Models; + Workflows = copy.Workflows; } public Permissions( @@ -41,7 +42,7 @@ namespace GitHub.Actions.WorkflowParser bool includeModels) { Actions = permissionLevel; - ArtifactMetadata = permissionLevel; + ArtifactMetadata = permissionLevel; Attestations = includeAttestations ? permissionLevel : PermissionLevel.NoAccess; Checks = permissionLevel; Contents = permissionLevel; @@ -56,9 +57,11 @@ namespace GitHub.Actions.WorkflowParser SecurityEvents = permissionLevel; IdToken = includeIdToken ? permissionLevel : PermissionLevel.NoAccess; // Models must not have higher permissions than Read - Models = includeModels - ? (permissionLevel == PermissionLevel.Write ? PermissionLevel.Read : permissionLevel) + Models = includeModels + ? (permissionLevel == PermissionLevel.Write ? PermissionLevel.Read : permissionLevel) : PermissionLevel.NoAccess; + // Workflows is excluded from write-all / read-all; must be explicitly requested + Workflows = PermissionLevel.NoAccess; } private static KeyValuePair[] ComparisonKeyMapping(Permissions left, Permissions right) @@ -81,6 +84,7 @@ namespace GitHub.Actions.WorkflowParser new KeyValuePair("security-events", (left.SecurityEvents, right.SecurityEvents)), new KeyValuePair("id-token", (left.IdToken, right.IdToken)), new KeyValuePair("models", (left.Models, right.Models)), + new KeyValuePair("workflows", (left.Workflows, right.Workflows)), }; } @@ -196,6 +200,13 @@ namespace GitHub.Actions.WorkflowParser set; } + [DataMember(Name = "workflows", EmitDefaultValue = false)] + public PermissionLevel Workflows + { + get; + set; + } + public Permissions Clone() { return new Permissions(this); diff --git a/src/Sdk/WorkflowParser/WorkflowFeatures.cs b/src/Sdk/WorkflowParser/WorkflowFeatures.cs index c3fa33af7..44f760f29 100644 --- a/src/Sdk/WorkflowParser/WorkflowFeatures.cs +++ b/src/Sdk/WorkflowParser/WorkflowFeatures.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -41,6 +41,13 @@ namespace GitHub.Actions.WorkflowParser [DataMember(EmitDefaultValue = false)] public bool AllowModelsPermission { get; set; } + /// + /// Gets or sets a value indicating whether users may use the "workflows" permission. + /// Used during parsing only. + /// + [DataMember(EmitDefaultValue = false)] + public bool AllowWorkflowsPermission { get; set; } + /// /// Gets or sets a value indicating whether the expression function fromJson performs strict JSON parsing. /// Used during evaluation only. @@ -67,6 +74,7 @@ namespace GitHub.Actions.WorkflowParser Snapshot = false, // Default to false since this feature is still in an experimental phase StrictJsonParsing = false, // Default to false since this is temporary for telemetry purposes only AllowModelsPermission = false, // Default to false since we want this to be disabled for all non-production environments + AllowWorkflowsPermission = false, // Default to false; gated by feature flag for controlled rollout AllowServiceContainerCommand = false, // Default to false since this feature is gated by actions_service_container_command }; }