Skip to content

Commit c1d2021

Browse files
committed
feat(capi+go): implement API for banning the use of certain modules.
1 parent 2f51251 commit c1d2021

File tree

4 files changed

+113
-1
lines changed

4 files changed

+113
-1
lines changed

capi/include/yara_x.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,18 @@ enum YRX_RESULT yrx_compiler_add_source_with_origin(struct YRX_COMPILER *compile
260260
enum YRX_RESULT yrx_compiler_ignore_module(struct YRX_COMPILER *compiler,
261261
const char *module);
262262

263+
// Tell the compiler that a YARA module can't be used.
264+
//
265+
// Import statements for the banned module will cause an error. The error
266+
// message can be customized by using the given error title and message.
267+
//
268+
// If this function is called multiple times with the same module name,
269+
// the error title and message will be updated.
270+
enum YRX_RESULT yrx_compiler_ban_module(struct YRX_COMPILER *compiler,
271+
const char *module,
272+
const char *error_title,
273+
const char *error_msg);
274+
263275
// Creates a new namespace.
264276
//
265277
// Further calls to `yrx_compiler_add_source` will put the rules under the

capi/src/compiler.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,50 @@ pub unsafe extern "C" fn yrx_compiler_ignore_module(
159159
YRX_RESULT::SUCCESS
160160
}
161161

162+
/// Tell the compiler that a YARA module can't be used.
163+
///
164+
/// Import statements for the banned module will cause an error. The error
165+
/// message can be customized by using the given error title and message.
166+
///
167+
/// If this function is called multiple times with the same module name,
168+
/// the error title and message will be updated.
169+
#[no_mangle]
170+
pub unsafe extern "C" fn yrx_compiler_ban_module(
171+
compiler: *mut YRX_COMPILER,
172+
module: *const c_char,
173+
error_title: *const c_char,
174+
error_msg: *const c_char,
175+
) -> YRX_RESULT {
176+
let compiler = if let Some(compiler) = compiler.as_mut() {
177+
compiler
178+
} else {
179+
return YRX_RESULT::INVALID_ARGUMENT;
180+
};
181+
182+
let module = if let Ok(module) = CStr::from_ptr(module).to_str() {
183+
module
184+
} else {
185+
return YRX_RESULT::INVALID_ARGUMENT;
186+
};
187+
188+
let err_title = if let Ok(err_title) = CStr::from_ptr(error_title).to_str()
189+
{
190+
err_title
191+
} else {
192+
return YRX_RESULT::INVALID_ARGUMENT;
193+
};
194+
195+
let err_msg = if let Ok(err_msg) = CStr::from_ptr(error_msg).to_str() {
196+
err_msg
197+
} else {
198+
return YRX_RESULT::INVALID_ARGUMENT;
199+
};
200+
201+
compiler.inner.ban_module(module, err_title, err_msg);
202+
203+
YRX_RESULT::SUCCESS
204+
}
205+
162206
/// Creates a new namespace.
163207
///
164208
/// Further calls to `yrx_compiler_add_source` will put the rules under the

go/compiler.go

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,21 @@ func IgnoreModule(module string) CompileOption {
5151
}
5252
}
5353

54+
// BanModule is an option for [NewCompiler] and [Compile] that allows
55+
// banning the use of a given module.
56+
//
57+
// Import statements for the banned module will cause an error. The error
58+
// message can be customized by using the given error title and message.
59+
//
60+
// If this function is called multiple times with the same module name,
61+
// the error title and message will be updated.
62+
func BanModule(module string, errTitle string, errMessage string) CompileOption {
63+
return func(c *Compiler) error {
64+
c.bannedModules[module] = bannedModule{errTitle, errMessage}
65+
return nil
66+
}
67+
}
68+
5469
// RelaxedReSyntax is an option for [NewCompiler] and [Compile] that
5570
// determines whether the compiler should adopt a more relaxed approach
5671
// while parsing regular expressions.
@@ -104,7 +119,7 @@ type SourceOption func(opt *sourceOptions) error
104119
// The origin is usually the path of the file containing the source code,
105120
// but it can be any arbitrary string that conveys information of the
106121
// source's origin. This origin appears in error reports, for instance, if
107-
// if origin is "some_file.yar", error reports will look like:
122+
// origin is "some_file.yar", error reports will look like:
108123
//
109124
// error: syntax error
110125
// --> some_file.yar:4:17
@@ -194,20 +209,27 @@ func (c CompileError) Error() string {
194209
return c.Text
195210
}
196211

212+
type bannedModule struct {
213+
errTitle string
214+
errMsg string
215+
}
216+
197217
// Compiler represent a YARA compiler.
198218
type Compiler struct {
199219
cCompiler *C.YRX_COMPILER
200220
relaxedReSyntax bool
201221
errorOnSlowPattern bool
202222
errorOnSlowLoop bool
203223
ignoredModules map[string]bool
224+
bannedModules map[string]bannedModule
204225
vars map[string]interface{}
205226
}
206227

207228
// NewCompiler creates a new compiler.
208229
func NewCompiler(opts ...CompileOption) (*Compiler, error) {
209230
c := &Compiler{
210231
ignoredModules: make(map[string]bool),
232+
bannedModules: make(map[string]bannedModule),
211233
vars: make(map[string]interface{}),
212234
}
213235

@@ -244,6 +266,9 @@ func (c *Compiler) initialize() error {
244266
for name, _ := range c.ignoredModules {
245267
c.ignoreModule(name)
246268
}
269+
for name, v := range c.bannedModules {
270+
c.banModule(name, v.errTitle, v.errMsg)
271+
}
247272
for ident, value := range c.vars {
248273
if err := c.DefineGlobal(ident, value); err != nil {
249274
return err
@@ -325,6 +350,23 @@ func (c *Compiler) ignoreModule(module string) {
325350
runtime.KeepAlive(c)
326351
}
327352

353+
func (c *Compiler) banModule(module, error_title, error_message string) {
354+
cModule := C.CString(module)
355+
defer C.free(unsafe.Pointer(cModule))
356+
357+
cErrTitle := C.CString(error_title)
358+
defer C.free(unsafe.Pointer(cErrTitle))
359+
360+
cErrMsg := C.CString(error_message)
361+
defer C.free(unsafe.Pointer(cErrMsg))
362+
363+
result := C.yrx_compiler_ban_module(c.cCompiler, cModule, cErrTitle, cErrMsg)
364+
if result != C.SUCCESS {
365+
panic("yrx_compiler_add_unsupported_module failed")
366+
}
367+
runtime.KeepAlive(c)
368+
}
369+
328370
// NewNamespace creates a new namespace.
329371
//
330372
// Later calls to [Compiler.AddSource] will put the rules under the newly created

go/compiler_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,20 @@ func TestUnsupportedModules(t *testing.T) {
3131
assert.Len(t, scanResults.MatchingRules(), 1)
3232
}
3333

34+
func TestBannedModules(t *testing.T) {
35+
_, err := Compile(
36+
`import "pe"`,
37+
BanModule("pe", "pe module is banned", "pe module was used here"))
38+
39+
expected := `error[E100]: pe module is banned
40+
--> line:1:1
41+
|
42+
1 | import "pe"
43+
| ^^^^^^^^^^^ pe module was used here
44+
|`
45+
assert.EqualError(t, err, expected)
46+
}
47+
3448
func TestRelaxedReSyntax(t *testing.T) {
3549
r, err := Compile(`
3650
rule test { strings: $a = /\Release/ condition: $a }`,

0 commit comments

Comments
 (0)