#nullable enable using System; using System.Linq; using GitHub.Actions.WorkflowParser.ObjectTemplating; namespace GitHub.Actions.WorkflowParser.Conversion { internal static class PermissionsHelper { /// /// Validates permissions requested in a reusable workflow do not exceed allowed permissions /// /// The template context /// The reusable workflow job /// (Optional) Used when formatting errors related to an embedded job within the reusable workflow /// The permissions within the reusable workflow file. These may be defined either at the root of the file, or may be defined on a job within the file. /// (Optional) The max permissions explicitly allowed by the caller /// The default permissions policy /// Indicates whether the reusable workflow exists within the same trust boundary (e.g. enterprise/organization) as a the root workflow internal static void ValidateEmbeddedPermissions( TemplateContext context, ReusableWorkflowJob workflowJob, IJob? embeddedJob, Permissions requested, Permissions? explicitMax, string permissionsPolicy, bool isTrusted) { if (requested == null) { return; } var effectiveMax = explicitMax ?? CreatePermissionsFromPolicy(context, permissionsPolicy, includeIdToken: isTrusted, includeModels: context.GetFeatures().AllowModelsPermission); if (requested.ViolatesMaxPermissions(effectiveMax, out var permissionLevelViolations)) { var requestedStr = string.Join(", ", permissionLevelViolations.Select(x => x.RequestedPermissionLevelString())); var allowedStr = string.Join(", ", permissionLevelViolations.Select(x => x.AllowedPermissionLevelString())); if (embeddedJob != null) { context.Error(workflowJob.Id, $"Error calling workflow '{workflowJob.Ref}'. The nested job '{embeddedJob.Id!.Value}' is requesting '{requestedStr}', but is only allowed '{allowedStr}'."); } else { context.Error(workflowJob.Id, $"Error calling workflow '{workflowJob.Ref}'. The workflow is requesting '{requestedStr}', but is only allowed '{allowedStr}'."); } } } /// /// Creates permissions based on policy /// /// The template context /// The permissions policy /// Indicates whether the permissions should include an ID token private static Permissions CreatePermissionsFromPolicy( TemplateContext context, string permissionsPolicy, bool includeIdToken, bool includeModels) { switch (permissionsPolicy) { case WorkflowConstants.PermissionsPolicy.LimitedRead: return new Permissions(PermissionLevel.NoAccess, includeIdToken: false, includeAttestations: false, includeModels: false) { Contents = PermissionLevel.Read, Packages = PermissionLevel.Read, }; case WorkflowConstants.PermissionsPolicy.Write: return new Permissions(PermissionLevel.Write, includeIdToken: includeIdToken, includeAttestations: true, includeModels: includeModels); default: throw new ArgumentException($"Unexpected permission policy: '{permissionsPolicy}'"); } } } }