Skip to content

Commit c413ffb

Browse files
authored
Merge pull request #36 from Tcode-Motion/security/fix-filesystem-capabilities-8953389804118506594
🔒 fix(stdlib): enforce FileSystem capability checks in file.rs
2 parents f4c6760 + 6db1488 commit c413ffb

2 files changed

Lines changed: 74 additions & 26 deletions

File tree

fix_file.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/bin/bash
2+
# A script to patch file.rs

stdlib/src/file.rs

Lines changed: 72 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ use crate::{StdFunction, StdlibModule, StdlibRegistry};
22
use std::collections::HashMap;
33
use std::fs;
44
use std::rc::Rc;
5-
use techscript_runtime::{error::RuntimeError, value::RuntimeValue};
5+
use techscript_runtime::{
6+
context::Capability,
7+
error::{RuntimeError, RuntimeErrorKind},
8+
value::RuntimeValue,
9+
};
610

711
impl StdlibRegistry {
812
pub fn register_file(&mut self) {
@@ -14,12 +18,22 @@ impl StdlibRegistry {
1418
Rc::new(StdFunction {
1519
name: "read".to_string(),
1620
arity: 1,
17-
callback: |_ctx, args| {
21+
callback: |ctx, args| {
22+
if !ctx.config.capabilities.contains(&Capability::FileSystem) {
23+
return Err(RuntimeError::new(
24+
RuntimeErrorKind::InvalidOperation(
25+
"Security policy violation: FileSystem capability is denied"
26+
.to_string(),
27+
),
28+
None,
29+
None,
30+
));
31+
}
1832
let path = match &args[0] {
1933
RuntimeValue::Str(s) => s.clone(),
2034
_ => {
2135
return Err(RuntimeError::new(
22-
techscript_runtime::error::RuntimeErrorKind::TypeMismatch {
36+
RuntimeErrorKind::TypeMismatch {
2337
expected: "string".to_string(),
2438
found: "other".to_string(),
2539
},
@@ -30,9 +44,7 @@ impl StdlibRegistry {
3044
};
3145
let content = fs::read_to_string(&path).map_err(|e| {
3246
RuntimeError::new(
33-
techscript_runtime::error::RuntimeErrorKind::InvalidOperation(
34-
e.to_string(),
35-
),
47+
RuntimeErrorKind::InvalidOperation(e.to_string()),
3648
None,
3749
None,
3850
)
@@ -47,12 +59,22 @@ impl StdlibRegistry {
4759
Rc::new(StdFunction {
4860
name: "write".to_string(),
4961
arity: 2,
50-
callback: |_ctx, args| {
62+
callback: |ctx, args| {
63+
if !ctx.config.capabilities.contains(&Capability::FileSystem) {
64+
return Err(RuntimeError::new(
65+
RuntimeErrorKind::InvalidOperation(
66+
"Security policy violation: FileSystem capability is denied"
67+
.to_string(),
68+
),
69+
None,
70+
None,
71+
));
72+
}
5173
let path = match &args[0] {
5274
RuntimeValue::Str(s) => s.clone(),
5375
_ => {
5476
return Err(RuntimeError::new(
55-
techscript_runtime::error::RuntimeErrorKind::TypeMismatch {
77+
RuntimeErrorKind::TypeMismatch {
5678
expected: "string".to_string(),
5779
found: "other".to_string(),
5880
},
@@ -65,7 +87,7 @@ impl StdlibRegistry {
6587
RuntimeValue::Str(s) => s.clone(),
6688
_ => {
6789
return Err(RuntimeError::new(
68-
techscript_runtime::error::RuntimeErrorKind::TypeMismatch {
90+
RuntimeErrorKind::TypeMismatch {
6991
expected: "string".to_string(),
7092
found: "other".to_string(),
7193
},
@@ -76,9 +98,7 @@ impl StdlibRegistry {
7698
};
7799
fs::write(&path, &content).map_err(|e| {
78100
RuntimeError::new(
79-
techscript_runtime::error::RuntimeErrorKind::InvalidOperation(
80-
e.to_string(),
81-
),
101+
RuntimeErrorKind::InvalidOperation(e.to_string()),
82102
None,
83103
None,
84104
)
@@ -93,12 +113,22 @@ impl StdlibRegistry {
93113
Rc::new(StdFunction {
94114
name: "copy".to_string(),
95115
arity: 2,
96-
callback: |_ctx, args| {
116+
callback: |ctx, args| {
117+
if !ctx.config.capabilities.contains(&Capability::FileSystem) {
118+
return Err(RuntimeError::new(
119+
RuntimeErrorKind::InvalidOperation(
120+
"Security policy violation: FileSystem capability is denied"
121+
.to_string(),
122+
),
123+
None,
124+
None,
125+
));
126+
}
97127
let src = match &args[0] {
98128
RuntimeValue::Str(s) => s.clone(),
99129
_ => {
100130
return Err(RuntimeError::new(
101-
techscript_runtime::error::RuntimeErrorKind::TypeMismatch {
131+
RuntimeErrorKind::TypeMismatch {
102132
expected: "string".to_string(),
103133
found: "other".to_string(),
104134
},
@@ -111,7 +141,7 @@ impl StdlibRegistry {
111141
RuntimeValue::Str(s) => s.clone(),
112142
_ => {
113143
return Err(RuntimeError::new(
114-
techscript_runtime::error::RuntimeErrorKind::TypeMismatch {
144+
RuntimeErrorKind::TypeMismatch {
115145
expected: "string".to_string(),
116146
found: "other".to_string(),
117147
},
@@ -122,9 +152,7 @@ impl StdlibRegistry {
122152
};
123153
fs::copy(&src, &dest).map_err(|e| {
124154
RuntimeError::new(
125-
techscript_runtime::error::RuntimeErrorKind::InvalidOperation(
126-
e.to_string(),
127-
),
155+
RuntimeErrorKind::InvalidOperation(e.to_string()),
128156
None,
129157
None,
130158
)
@@ -139,12 +167,22 @@ impl StdlibRegistry {
139167
Rc::new(StdFunction {
140168
name: "remove".to_string(),
141169
arity: 1,
142-
callback: |_ctx, args| {
170+
callback: |ctx, args| {
171+
if !ctx.config.capabilities.contains(&Capability::FileSystem) {
172+
return Err(RuntimeError::new(
173+
RuntimeErrorKind::InvalidOperation(
174+
"Security policy violation: FileSystem capability is denied"
175+
.to_string(),
176+
),
177+
None,
178+
None,
179+
));
180+
}
143181
let path = match &args[0] {
144182
RuntimeValue::Str(s) => s.clone(),
145183
_ => {
146184
return Err(RuntimeError::new(
147-
techscript_runtime::error::RuntimeErrorKind::TypeMismatch {
185+
RuntimeErrorKind::TypeMismatch {
148186
expected: "string".to_string(),
149187
found: "other".to_string(),
150188
},
@@ -155,9 +193,7 @@ impl StdlibRegistry {
155193
};
156194
fs::remove_file(&path).map_err(|e| {
157195
RuntimeError::new(
158-
techscript_runtime::error::RuntimeErrorKind::InvalidOperation(
159-
e.to_string(),
160-
),
196+
RuntimeErrorKind::InvalidOperation(e.to_string()),
161197
None,
162198
None,
163199
)
@@ -172,12 +208,22 @@ impl StdlibRegistry {
172208
Rc::new(StdFunction {
173209
name: "exists".to_string(),
174210
arity: 1,
175-
callback: |_ctx, args| {
211+
callback: |ctx, args| {
212+
if !ctx.config.capabilities.contains(&Capability::FileSystem) {
213+
return Err(RuntimeError::new(
214+
RuntimeErrorKind::InvalidOperation(
215+
"Security policy violation: FileSystem capability is denied"
216+
.to_string(),
217+
),
218+
None,
219+
None,
220+
));
221+
}
176222
let path = match &args[0] {
177223
RuntimeValue::Str(s) => s.clone(),
178224
_ => {
179225
return Err(RuntimeError::new(
180-
techscript_runtime::error::RuntimeErrorKind::TypeMismatch {
226+
RuntimeErrorKind::TypeMismatch {
181227
expected: "string".to_string(),
182228
found: "other".to_string(),
183229
},
@@ -197,7 +243,7 @@ impl StdlibRegistry {
197243
name: "std.file".to_string(),
198244
version: "1.0.0".to_string(),
199245
exports,
200-
required_capabilities: Vec::new(),
246+
required_capabilities: vec![Capability::FileSystem],
201247
},
202248
);
203249
}

0 commit comments

Comments
 (0)