Skip to content

Commit 35a5fe8

Browse files
committed
[SEC] implement robust CORS configuration with settings support
Dependent on: odoo/aw-webui#1
1 parent 9a8802a commit 35a5fe8

7 files changed

Lines changed: 95 additions & 18 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ NDK
1010
*.sqlite*
1111
*.db
1212
*.db-journal
13+
14+
.vscode

Cargo.lock

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

aw-server/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ aw-datastore = { path = "../aw-datastore" }
3434
aw-models = { path = "../aw-models" }
3535
aw-transform = { path = "../aw-transform" }
3636
aw-query = { path = "../aw-query" }
37+
regex = "1.12.3"
3738

3839
[target.'cfg(target_os="linux")'.dependencies]
3940
sd-notify = "0.4.2"

aw-server/src/config.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,24 @@ pub struct AWConfig {
3636
#[serde(default = "default_cors")]
3737
pub cors_regex: Vec<String>,
3838

39+
#[serde(default = "default_true")]
40+
pub allow_aw_chrome_extension: bool,
41+
42+
#[serde(default = "default_false")]
43+
pub allow_all_mozilla_extension: bool,
44+
45+
#[serde(default = "default_cors")]
46+
pub cors_from_settings: Vec<String>,
47+
48+
#[serde(default = "default_cors")]
49+
pub cors_regex_from_settings: Vec<String>,
50+
51+
#[serde(skip)]
52+
pub allow_aw_chrome_extension_from_settings: Option<bool>,
53+
54+
#[serde(skip)]
55+
pub allow_all_mozilla_extension_from_settings: Option<bool>,
56+
3957
// A mapping of watcher names to paths where the
4058
// custom visualizations are located.
4159
#[serde(default = "default_custom_static")]
@@ -50,6 +68,12 @@ impl Default for AWConfig {
5068
testing: default_testing(),
5169
cors: default_cors(),
5270
cors_regex: default_cors(),
71+
allow_aw_chrome_extension: default_true(),
72+
allow_all_mozilla_extension: default_false(),
73+
cors_from_settings: default_cors(),
74+
cors_regex_from_settings: default_cors(),
75+
allow_aw_chrome_extension_from_settings: None,
76+
allow_all_mozilla_extension_from_settings: None,
5377
custom_static: default_custom_static(),
5478
}
5579
}
@@ -91,6 +115,14 @@ fn default_testing() -> bool {
91115
is_testing()
92116
}
93117

118+
fn default_true() -> bool {
119+
true
120+
}
121+
122+
fn default_false() -> bool {
123+
false
124+
}
125+
94126
fn default_port() -> u16 {
95127
if is_testing() {
96128
5666

aw-server/src/endpoints/cors.rs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,29 @@ pub fn cors(config: &AWConfig) -> rocket_cors::Cors {
88
let root_url_localhost = format!("http://localhost:{}", config.port);
99
let mut allowed_exact_origins = vec![root_url, root_url_localhost];
1010
allowed_exact_origins.extend(config.cors.clone());
11+
allowed_exact_origins.extend(config.cors_from_settings.clone());
1112

12-
if config.testing {
13-
allowed_exact_origins.push("http://127.0.0.1:27180".to_string());
14-
allowed_exact_origins.push("http://localhost:27180".to_string());
13+
let mut allowed_regex_origins = config.cors_regex.clone();
14+
allowed_regex_origins.extend(config.cors_regex_from_settings.clone());
15+
16+
// Settings-based flags override file-based config flags
17+
let allow_chrome = config.allow_aw_chrome_extension_from_settings
18+
.unwrap_or(config.allow_aw_chrome_extension);
19+
if allow_chrome {
20+
allowed_regex_origins.push("chrome-extension://nglaklhklhcoonedhgnpgddginnjdadi".to_string());
1521
}
16-
let mut allowed_regex_origins = vec![
17-
"chrome-extension://nglaklhklhcoonedhgnpgddginnjdadi".to_string(),
18-
// Every version of a mozilla extension has its own ID to avoid fingerprinting, so we
19-
// unfortunately have to allow all extensions to have access to aw-server
20-
"moz-extension://.*".to_string(),
21-
];
22-
allowed_regex_origins.extend(config.cors_regex.clone());
22+
23+
let allow_mozilla = config.allow_all_mozilla_extension_from_settings
24+
.unwrap_or(config.allow_all_mozilla_extension);
25+
if allow_mozilla {
26+
allowed_regex_origins.push("moz-extension://.*".to_string());
27+
}
28+
2329
if config.testing {
24-
allowed_regex_origins.push("chrome-extension://.*".to_string());
30+
allowed_exact_origins.extend(vec![
31+
"http://127.0.0.1:27180".to_string(),
32+
"http://localhost:27180".to_string(),
33+
]);
2534
}
2635

2736
let allowed_origins = AllowedOrigins::some(&allowed_exact_origins, &allowed_regex_origins);
@@ -31,7 +40,6 @@ pub fn cors(config: &AWConfig) -> rocket_cors::Cors {
3140
.collect();
3241
let allowed_headers = AllowedHeaders::all(); // TODO: is this unsafe?
3342

34-
// You can also deserialize this
3543
rocket_cors::CorsOptions {
3644
allowed_origins,
3745
allowed_methods,

aw-server/src/endpoints/mod.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,44 @@ fn get_file(file: PathBuf, state: &State<ServerState>) -> Option<(ContentType, V
127127
Some((content_type, asset))
128128
}
129129

130-
pub fn build_rocket(server_state: ServerState, config: AWConfig) -> rocket::Rocket<rocket::Build> {
130+
pub fn build_rocket(server_state: ServerState, mut config: AWConfig) -> rocket::Rocket<rocket::Build> {
131131
info!(
132132
"Starting aw-server-rust at {}:{}",
133133
config.address, config.port
134134
);
135+
{
136+
let db = server_state.datastore.lock().unwrap();
137+
let parse_cors_list = |raw: String| -> Vec<String> {
138+
serde_json::from_str::<String>(&raw)
139+
.unwrap_or_default()
140+
.split(',')
141+
.map(|s| s.trim().to_string())
142+
.filter(|s| !s.is_empty())
143+
.collect()
144+
};
145+
let parse_bool = |raw: String| -> Option<bool> {
146+
serde_json::from_str::<bool>(&raw)
147+
.ok()
148+
.or_else(|| {
149+
if raw == "true" { Some(true) }
150+
else if raw == "false" { Some(false) }
151+
else { None }
152+
})
153+
};
154+
155+
if let Ok(raw) = db.get_key_value("settings.cors") {
156+
config.cors_from_settings = parse_cors_list(raw);
157+
}
158+
if let Ok(raw) = db.get_key_value("settings.cors_regex") {
159+
config.cors_regex_from_settings = parse_cors_list(raw);
160+
}
161+
if let Ok(raw) = db.get_key_value("settings.allow_aw_chrome_extension") {
162+
config.allow_aw_chrome_extension_from_settings = parse_bool(raw);
163+
}
164+
if let Ok(raw) = db.get_key_value("settings.allow_all_mozilla_extension") {
165+
config.allow_all_mozilla_extension_from_settings = parse_bool(raw);
166+
}
167+
} // lock released here
135168
let cors = cors::cors(&config);
136169
let hostcheck = hostcheck::HostCheck::new(&config);
137170
let custom_static = config.custom_static.clone();

0 commit comments

Comments
 (0)