Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T05:55:17.144Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

check_permissions.rs

75 lines · 2.7 KB · rust
1use annotate_snippets::{AnnotationKind, Group, Level, Snippet};
2use serde_yaml::Value;
3use std::{ops::Range, path::Path};
4
5pub enum PermissionsError {
6    /// No top-level `permissions:` key.
7    Missing,
8    /// Top-level default grants more than `contents: read`.
9    ExcessiveDefault,
10}
11
12/// Validates a workflow's top-level (default) `permissions:`.
13///
14/// Every workflow must declare one, and it may grant at most `contents: read`;
15/// jobs that need more must request it at the job level.
16pub fn validate_permissions(workflow: &Value) -> Result<(), PermissionsError> {
17    let Some(permissions) = workflow.get("permissions") else {
18        return Err(PermissionsError::Missing);
19    };
20
21    let is_minimal = match permissions {
22        Value::Mapping(mapping) => mapping.iter().all(|(scope, level)| {
23            let level = level.as_str();
24            level == Some("none") || (scope.as_str() == Some("contents") && level == Some("read"))
25        }),
26        // String forms such as `read-all`/`write-all` always exceed the allowance.
27        _ => false,
28    };
29
30    if is_minimal {
31        Ok(())
32    } else {
33        Err(PermissionsError::ExcessiveDefault)
34    }
35}
36
37impl PermissionsError {
38    pub fn annotation_group<'a>(&self, file_path: &Path, raw_content: &'a str) -> Group<'a> {
39        let (title, span, label) = match self {
40            PermissionsError::Missing => (
41                "Workflow is missing a top-level `permissions:` key",
42                first_line_span(raw_content, "name:"),
43                "Add a top-level `permissions:` key so this workflow defaults to the least privilege it needs",
44            ),
45            PermissionsError::ExcessiveDefault => (
46                "Top-level workflow permissions must grant at most `contents: read`",
47                first_line_span(raw_content, "permissions:"),
48                "Lower the default to `contents: read` (or `{}`) and move elevated permissions to the jobs that need them",
49            ),
50        };
51
52        Level::ERROR.primary_title(title).element(
53            Snippet::source(raw_content)
54                .path(file_path.display().to_string())
55                .annotations(std::iter::once(
56                    AnnotationKind::Primary.span(span).label(label),
57                )),
58        )
59    }
60}
61
62/// Span of the first column-0 line starting with `prefix`, else empty.
63fn first_line_span(raw_content: &str, prefix: &str) -> Range<usize> {
64    let mut offset = 0;
65    for line in raw_content.lines() {
66        let start = offset;
67        offset += line.len() + 1;
68        if line.starts_with(prefix) {
69            return start..start + line.len();
70        }
71    }
72
73    Default::default()
74}
75
Served at tenant.openagents/omega Member data and write actions are omitted.