fix: tighten preview path (#7541)

This commit is contained in:
Ruben Fiszel
2026-01-12 06:21:05 +01:00
committed by GitHub
parent de2f5d3432
commit dca7e16532
3 changed files with 58 additions and 2 deletions
+1 -1
View File
@@ -1 +1 @@
c8e8a6df19203acc2cef1aebd1bd4157f2439cbf
0c02f5aba882b2e16361836835545cbf9e9ab275
+4 -1
View File
@@ -64,7 +64,7 @@ use crate::{
concurrency_groups::join_concurrency_key,
db::{ApiAuthed, DB},
triggers::trigger_helpers::RunnableId,
users::{get_scope_tags, require_owner_of_path, OptAuthed},
users::{get_scope_tags, require_owner_of_path, require_path_read_access_for_preview, OptAuthed},
utils::{check_scopes, content_plain, require_super_admin},
};
use anyhow::Context;
@@ -6018,6 +6018,7 @@ async fn run_preview_script(
"Operators cannot run preview jobs for security reasons".to_string(),
));
}
require_path_read_access_for_preview(&authed, &preview.path)?;
let scheduled_for = run_query.get_scheduled_for(&db).await?;
let tag = run_query.tag.clone().or(preview.tag.clone());
check_tag_available_for_workspace(&db, &w_id, &tag, &authed).await?;
@@ -6160,6 +6161,7 @@ async fn run_bundle_preview_script(
let data = data.map_err(to_anyhow)?;
if name == "preview" {
let preview: Preview = serde_json::from_slice(&data).map_err(to_anyhow)?;
require_path_read_access_for_preview(&authed, &preview.path)?;
format = preview
.format
.and_then(|s| BundleFormat::from_string(&s))
@@ -6771,6 +6773,7 @@ async fn run_preview_flow_job(
"Operators cannot run preview jobs for security reasons".to_string(),
));
}
require_path_read_access_for_preview(&authed, &raw_flow.path)?;
let scheduled_for = run_query.get_scheduled_for(&db).await?;
let tag = run_query.tag.clone().or(raw_flow.tag.clone());
check_tag_available_for_workspace(&db, &w_id, &tag, &authed).await?;
+53
View File
@@ -929,6 +929,59 @@ pub fn require_owner_of_path(authed: &ApiAuthed, path: &str) -> Result<()> {
}
}
/// Checks that a user has at least read access to the path for preview jobs.
/// This prevents privilege escalation where a user could run preview code
/// under a path they don't have access to.
pub fn require_path_read_access_for_preview(authed: &ApiAuthed, path: &Option<String>) -> Result<()> {
let Some(path) = path else {
return Ok(());
};
if authed.is_admin {
return Ok(());
}
if path.is_empty() {
return Ok(());
}
let splitted: Vec<&str> = path.split('/').collect();
if splitted.len() < 2 {
return Err(Error::BadRequest(format!(
"Invalid path format for preview job: {}",
path
)));
}
match splitted[0] {
"u" => {
if splitted[1] == authed.username {
Ok(())
} else {
Err(Error::BadRequest(format!(
"You can only run preview jobs in your own namespace (u/{}) or in folders you have read access to",
authed.username
)))
}
}
"f" => {
let folder = splitted[1];
if authed.folders.iter().any(|(f, _, _)| f == folder) {
Ok(())
} else {
Err(Error::BadRequest(format!(
"You do not have read access to folder '{}'. Preview jobs require at least read access to the target folder.",
folder
)))
}
}
_ => Err(Error::BadRequest(format!(
"Invalid path format for preview job: {}. Path must start with 'u/' or 'f/'",
path
))),
}
}
pub fn get_perm_in_extra_perms_for_authed(
v: serde_json::Value,
authed: &ApiAuthed,