Move build_id to bolts (#649)

* Drop the build_id depedency and move to bolts

* tabs->spaces

* clippy build_id fixes

* frida clippy

Co-authored-by: Dominik Maier <dmnk@google.com>
This commit is contained in:
Andrea Fioraldi 2022-05-27 01:05:03 +02:00 committed by GitHub
parent 763ed9a3e5
commit a544bc042d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 220 additions and 106 deletions

View File

@ -115,9 +115,9 @@ void func1() {
// Export this symbol // Export this symbol
#ifdef _WIN32 #ifdef _WIN32
# define HARNESS_EXPORTS __declspec(dllexport) # define HARNESS_EXPORTS __declspec(dllexport)
#else #else
# define HARNESS_EXPORTS # define HARNESS_EXPORTS
#endif #endif
// Entry point for LibFuzzer. // Entry point for LibFuzzer.

View File

@ -48,12 +48,12 @@ make -C libpng-1.6.37
cc -c "${PROJECT_DIR}/libfuzzer_main.c" cc -c "${PROJECT_DIR}/libfuzzer_main.c"
# Build the libpng harness # Build the libpng harness
c++ \ c++ \
../libfuzzer_libpng/harness.cc \ ../libfuzzer_libpng/harness.cc \
./libpng-1.6.37/.libs/libpng16.a \ ./libpng-1.6.37/.libs/libpng16.a \
./libfuzzer_main.o \ ./libfuzzer_main.o \
-I./libpng-1.6.37/ \ -I./libpng-1.6.37/ \
-o ${FUZZER_NAME} \ -o ${FUZZER_NAME} \
-lm -lz -lm -lz
''' '''
dependencies = ["libpng"] dependencies = ["libpng"]

View File

@ -48,12 +48,12 @@ make -C libpng-1.6.37
cc -c "${PROJECT_DIR}/libfuzzer_main.c" cc -c "${PROJECT_DIR}/libfuzzer_main.c"
# Build the libpng harness # Build the libpng harness
c++ \ c++ \
../libfuzzer_libpng/harness.cc \ ../libfuzzer_libpng/harness.cc \
./libpng-1.6.37/.libs/libpng16.a \ ./libpng-1.6.37/.libs/libpng16.a \
./libfuzzer_main.o \ ./libfuzzer_main.o \
-I./libpng-1.6.37/ \ -I./libpng-1.6.37/ \
-o ${FUZZER_NAME} \ -o ${FUZZER_NAME} \
-lm -lz -lm -lz
''' '''
dependencies = ["libpng"] dependencies = ["libpng"]

View File

@ -5,9 +5,9 @@
struct my_error_mgr { struct my_error_mgr {
struct jpeg_error_mgr pub; /* "public" fields */ struct jpeg_error_mgr pub; /* "public" fields */
jmp_buf setjmp_buffer; /* for return to caller */ jmp_buf setjmp_buffer; /* for return to caller */
}; };
typedef struct my_error_mgr *my_error_ptr; typedef struct my_error_mgr *my_error_ptr;
@ -16,18 +16,18 @@ typedef struct my_error_mgr *my_error_ptr;
* Here's the routine that will replace the standard error_exit method: * Here's the routine that will replace the standard error_exit method:
*/ */
METHODDEF(void) METHODDEF(void)
my_error_exit(j_common_ptr cinfo) my_error_exit(j_common_ptr cinfo)
{ {
/* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
my_error_ptr myerr = (my_error_ptr)cinfo->err; my_error_ptr myerr = (my_error_ptr)cinfo->err;
/* Always display the message. */ /* Always display the message. */
/* We could postpone this until after returning, if we chose. */ /* We could postpone this until after returning, if we chose. */
(*cinfo->err->output_message) (cinfo); (*cinfo->err->output_message) (cinfo);
/* Return control to the setjmp point */ /* Return control to the setjmp point */
longjmp(myerr->setjmp_buffer, 1); longjmp(myerr->setjmp_buffer, 1);
} }
@ -35,48 +35,48 @@ my_error_exit(j_common_ptr cinfo)
int do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, const uint8_t *input, size_t len) int do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, const uint8_t *input, size_t len)
{ {
struct my_error_mgr jerr; struct my_error_mgr jerr;
/* More stuff */ /* More stuff */
JSAMPARRAY buffer; /* Output row buffer */ JSAMPARRAY buffer; /* Output row buffer */
int row_stride; /* physical row width in output buffer */ int row_stride; /* physical row width in output buffer */
/* Step 1: allocate and initialize JPEG decompression object */ /* Step 1: allocate and initialize JPEG decompression object */
/* We set up the normal JPEG error routines, then override error_exit. */ /* We set up the normal JPEG error routines, then override error_exit. */
cinfo->err = jpeg_std_error(&jerr.pub); cinfo->err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit; jerr.pub.error_exit = my_error_exit;
/* Establish the setjmp return context for my_error_exit to use. */ /* Establish the setjmp return context for my_error_exit to use. */
if (setjmp(jerr.setjmp_buffer)) { if (setjmp(jerr.setjmp_buffer)) {
jpeg_destroy_decompress(cinfo); jpeg_destroy_decompress(cinfo);
return 0; return 0;
} }
/* Now we can initialize the JPEG decompression object. */ /* Now we can initialize the JPEG decompression object. */
jpeg_create_decompress(cinfo); jpeg_create_decompress(cinfo);
/* Step 2: specify data source (eg, a file) */ /* Step 2: specify data source (eg, a file) */
jpeg_mem_src(cinfo,input, len ); jpeg_mem_src(cinfo,input, len );
/* Step 3: read file parameters with jpeg_read_header() */ /* Step 3: read file parameters with jpeg_read_header() */
(void)jpeg_read_header(cinfo, TRUE); (void)jpeg_read_header(cinfo, TRUE);
/* Step 4: set parameters for decompression */ /* Step 4: set parameters for decompression */
/* In this example, we don't need to change any of the defaults set by /* In this example, we don't need to change any of the defaults set by
* jpeg_read_header(), so we do nothing here. * jpeg_read_header(), so we do nothing here.
*/ */
/* Step 5: Start decompressor */ /* Step 5: Start decompressor */
(void)jpeg_start_decompress(cinfo); (void)jpeg_start_decompress(cinfo);
/* JSAMPLEs per row in output buffer */ /* JSAMPLEs per row in output buffer */
row_stride = cinfo->output_width * cinfo->output_components; row_stride = cinfo->output_width * cinfo->output_components;
/* Make a one-row-high sample array that will go away when done with image */ /* Make a one-row-high sample array that will go away when done with image */
buffer = (*cinfo->mem->alloc_sarray) buffer = (*cinfo->mem->alloc_sarray)
((j_common_ptr)cinfo, JPOOL_IMAGE, row_stride, 1); ((j_common_ptr)cinfo, JPOOL_IMAGE, row_stride, 1);
/* Step 6: while (scan lines remain to be read) */ /* Step 6: while (scan lines remain to be read) */
/* jpeg_read_scanlines(...); */ /* jpeg_read_scanlines(...); */
while (cinfo->output_scanline < cinfo->output_height) { while (cinfo->output_scanline < cinfo->output_height) {
(void)jpeg_read_scanlines(cinfo, buffer, 1); (void)jpeg_read_scanlines(cinfo, buffer, 1);
/* Assume put_scanline_someplace wants a pointer and sample count. */ /* Assume put_scanline_someplace wants a pointer and sample count. */
//put_scanline_someplace(buffer[0], row_stride); //put_scanline_someplace(buffer[0], row_stride);
} }
/* Step 7: Finish decompression */ /* Step 7: Finish decompression */
(void)jpeg_finish_decompress(cinfo); (void)jpeg_finish_decompress(cinfo);
/* Step 8: Release JPEG decompression object */ /* Step 8: Release JPEG decompression object */
//jpeg_destroy_decompress(cinfo); //jpeg_destroy_decompress(cinfo);
return 1; return 1;
} }
@ -84,9 +84,9 @@ int do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, const uint8_t *input
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
struct jpeg_decompress_struct cinfo; struct jpeg_decompress_struct cinfo;
do_read_JPEG_file(&cinfo,data,size); do_read_JPEG_file(&cinfo,data,size);
return 0; return 0;
} }

View File

@ -22,7 +22,7 @@ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
free(img); free(img);
// if (x > 10000) free(img); // free crash // if (x > 10000) free(img); // free crash
return 0; return 0;
} }

View File

@ -1270,7 +1270,7 @@ STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
{ {
return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
} }
#endif #endif
@ -1280,15 +1280,15 @@ static FILE *stbi__fopen(char const *filename, char const *mode)
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
wchar_t wMode[64]; wchar_t wMode[64];
wchar_t wFilename[1024]; wchar_t wFilename[1024];
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)))
return 0; return 0;
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)))
return 0; return 0;
#if _MSC_VER >= 1400 #if _MSC_VER >= 1400
if (0 != _wfopen_s(&f, wFilename, wMode)) if (0 != _wfopen_s(&f, wFilename, wMode))
f = 0; f = 0;
#else #else
f = _wfopen(wFilename, wMode); f = _wfopen(wFilename, wMode);
#endif #endif
@ -5184,7 +5184,7 @@ static int stbi__png_is16(stbi__context *s)
stbi__png p; stbi__png p;
p.s = s; p.s = s;
if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) if (!stbi__png_info_raw(&p, NULL, NULL, NULL))
return 0; return 0;
if (p.depth != 16) { if (p.depth != 16) {
stbi__rewind(p.s); stbi__rewind(p.s);
return 0; return 0;

View File

@ -22,7 +22,7 @@ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
free(img); free(img);
// if (x > 10000) free(img); // free crash // if (x > 10000) free(img); // free crash
return 0; return 0;
} }

View File

@ -1270,7 +1270,7 @@ STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
{ {
return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
} }
#endif #endif
@ -1280,15 +1280,15 @@ static FILE *stbi__fopen(char const *filename, char const *mode)
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
wchar_t wMode[64]; wchar_t wMode[64];
wchar_t wFilename[1024]; wchar_t wFilename[1024];
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)))
return 0; return 0;
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)))
return 0; return 0;
#if _MSC_VER >= 1400 #if _MSC_VER >= 1400
if (0 != _wfopen_s(&f, wFilename, wMode)) if (0 != _wfopen_s(&f, wFilename, wMode))
f = 0; f = 0;
#else #else
f = _wfopen(wFilename, wMode); f = _wfopen(wFilename, wMode);
#endif #endif
@ -5184,7 +5184,7 @@ static int stbi__png_is16(stbi__context *s)
stbi__png p; stbi__png p;
p.s = s; p.s = s;
if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) if (!stbi__png_info_raw(&p, NULL, NULL, NULL))
return 0; return 0;
if (p.depth != 16) { if (p.depth != 16) {
stbi__rewind(p.s); stbi__rewind(p.s);
return 0; return 0;

View File

@ -22,7 +22,7 @@ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
free(img); free(img);
// if (x > 10000) free(img); // free crash // if (x > 10000) free(img); // free crash
return 0; return 0;
} }

View File

@ -1270,7 +1270,7 @@ STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
{ {
return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
} }
#endif #endif
@ -1280,15 +1280,15 @@ static FILE *stbi__fopen(char const *filename, char const *mode)
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
wchar_t wMode[64]; wchar_t wMode[64];
wchar_t wFilename[1024]; wchar_t wFilename[1024];
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)))
return 0; return 0;
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)))
return 0; return 0;
#if _MSC_VER >= 1400 #if _MSC_VER >= 1400
if (0 != _wfopen_s(&f, wFilename, wMode)) if (0 != _wfopen_s(&f, wFilename, wMode))
f = 0; f = 0;
#else #else
f = _wfopen(wFilename, wMode); f = _wfopen(wFilename, wMode);
#endif #endif
@ -5184,7 +5184,7 @@ static int stbi__png_is16(stbi__context *s)
stbi__png p; stbi__png p;
p.s = s; p.s = s;
if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) if (!stbi__png_info_raw(&p, NULL, NULL, NULL))
return 0; return 0;
if (p.depth != 16) { if (p.depth != 16) {
stbi__rewind(p.s); stbi__rewind(p.s);
return 0; return 0;

View File

@ -47,11 +47,11 @@ cd "${PROJECT_DIR}"
make -C libpng-1.6.37 make -C libpng-1.6.37
# Build the libpng harness # Build the libpng harness
c++ \ c++ \
./harness.cc \ ./harness.cc \
./libpng-1.6.37/.libs/libpng16.a \ ./libpng-1.6.37/.libs/libpng16.a \
-I./libpng-1.6.37/ \ -I./libpng-1.6.37/ \
-o ${FUZZER_NAME} \ -o ${FUZZER_NAME} \
-lm -lz -lm -lz
''' '''
dependencies = [ "libpng" ] dependencies = [ "libpng" ]

View File

@ -13,7 +13,7 @@ categories = ["development-tools::testing", "emulators", "embedded", "os", "no-s
[features] [features]
default = ["std", "derive", "llmp_compression", "rand_trait", "fork"] default = ["std", "derive", "llmp_compression", "rand_trait", "fork"]
std = ["serde_json", "serde_json/std", "hostname", "core_affinity", "nix", "serde/std", "bincode", "wait-timeout", "regex", "build_id", "uuid", "tui_monitor", "ctor", "backtrace"] # print, env, launcher ... support std = ["serde_json", "serde_json/std", "hostname", "core_affinity", "nix", "serde/std", "bincode", "wait-timeout", "regex", "byteorder", "once_cell", "uuid", "tui_monitor", "ctor", "backtrace"] # print, env, launcher ... support
derive = ["libafl_derive"] # provide derive(SerdeAny) macro. derive = ["libafl_derive"] # provide derive(SerdeAny) macro.
fork = [] # uses the fork() syscall to spawn children, instead of launching a new command, if supported by the OS (has no effect on Windows, no_std). fork = [] # uses the fork() syscall to spawn children, instead of launching a new command, if supported by the OS (has no effect on Windows, no_std).
rand_trait = ["rand_core"] # If set, libafl's rand implementations will implement `rand::Rng` rand_trait = ["rand_core"] # If set, libafl's rand implementations will implement `rand::Rng`
@ -73,8 +73,9 @@ hostname = { version = "^0.3", optional = true } # Is there really no gethostnam
rand_core = { version = "0.5.1", optional = true } # This dependency allows us to export our RomuRand as rand::Rng. We cannot update to the latest version because it breaks compatibility to microsoft lain. rand_core = { version = "0.5.1", optional = true } # This dependency allows us to export our RomuRand as rand::Rng. We cannot update to the latest version because it breaks compatibility to microsoft lain.
nix = { version = "0.23", optional = true } nix = { version = "0.23", optional = true }
regex = { version = "1", optional = true } regex = { version = "1", optional = true }
build_id = { version = "0.2.1", git = "https://github.com/domenukk/build_id", rev = "6a61943", optional = true }
uuid = { version = "0.8.2", optional = true, features = ["serde", "v4"] } uuid = { version = "0.8.2", optional = true, features = ["serde", "v4"] }
byteorder = { version = "1.2", optional = true }
once_cell = { version = "1.2", optional = true }
libm = "0.2.1" libm = "0.2.1"
tui = { version = "0.16", default-features = false, features = ['crossterm'], optional = true } tui = { version = "0.16", default-features = false, features = ['crossterm'], optional = true }
crossterm = { version = "0.20", optional = true } crossterm = { version = "0.20", optional = true }

View File

@ -0,0 +1,111 @@
//! Based on <https://github.com/alecmocatta/build_id>
//! (C) Alec Mocatta <alec@mocatta.net> under license MIT or Apache 2
use once_cell::sync::Lazy;
use std::{
any::TypeId,
env,
fs::File,
hash::{Hash, Hasher},
io,
};
use uuid::Uuid;
static BUILD_ID: Lazy<Uuid> = Lazy::new(calculate);
/// Returns a [Uuid] uniquely representing the build of the current binary.
///
/// This is intended to be used to check that different processes are indeed
/// invocations of identically laid out binaries.
///
/// As such:
/// * It is guaranteed to be identical within multiple invocations of the same
/// binary.
/// * It is guaranteed to be different across binaries with different code or
/// data segments or layout.
/// * Equality is unspecified if the binaries have identical code and data
/// segments and layout but differ immaterially (e.g. if a timestamp is included
/// in the binary at compile time).
///
/// # Examples
///
/// ```
/// # let remote_build_id = libafl::bolts::build_id::get();
/// let local_build_id = libafl::bolts::build_id::get();
/// if local_build_id == remote_build_id {
/// println!("We're running the same binary as remote!");
/// } else {
/// println!("We're running a different binary to remote");
/// }
/// ```
///
/// # Note
///
/// This looks first for linker-inserted build ID / binary UUIDs (i.e.
/// `.note.gnu.build-id` on Linux; `LC_UUID` in Mach-O; etc), falling back to
/// hashing the whole binary.
#[inline]
#[must_use]
pub fn get() -> Uuid {
*BUILD_ID
}
fn from_exe<H: Hasher>(mut hasher: H) -> Result<H, ()> {
#[cfg(not(target_arch = "wasm32"))]
{
if cfg!(miri) {
return Err(());
}
let file = File::open(env::current_exe().map_err(drop)?).map_err(drop)?;
let _ = io::copy(&mut &file, &mut HashWriter(&mut hasher)).map_err(drop)?;
Ok(hasher)
}
#[cfg(target_arch = "wasm32")]
{
let _ = &mut hasher;
Err(())
}
}
fn from_type_id<H: Hasher>(mut hasher: H) -> H {
fn type_id_of<T: 'static>(_: &T) -> TypeId {
TypeId::of::<T>()
}
TypeId::of::<()>().hash(&mut hasher);
TypeId::of::<u8>().hash(&mut hasher);
let a = |x: ()| x;
type_id_of(&a).hash(&mut hasher);
let b = |x: u8| x;
type_id_of(&b).hash(&mut hasher);
hasher
}
fn calculate() -> Uuid {
let hasher = xxhash_rust::xxh3::Xxh3::with_seed(0);
let hasher = from_exe(hasher.clone()).unwrap_or(hasher);
let mut hasher = from_type_id(hasher);
let mut bytes = [0; 16];
<byteorder::NativeEndian as byteorder::ByteOrder>::write_u64(&mut bytes[..8], hasher.finish());
hasher.write_u8(0);
<byteorder::NativeEndian as byteorder::ByteOrder>::write_u64(&mut bytes[8..], hasher.finish());
uuid::Builder::from_bytes(bytes)
.set_variant(uuid::Variant::RFC4122)
.set_version(uuid::Version::Random)
.build()
}
struct HashWriter<T: Hasher>(T);
impl<T: Hasher> io::Write for HashWriter<T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf);
Ok(buf.len())
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.write(buf).map(|_| ())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}

View File

@ -1,6 +1,8 @@
//! Bolts are no conceptual fuzzing elements, but they keep libafl-based fuzzers together. //! Bolts are no conceptual fuzzing elements, but they keep libafl-based fuzzers together.
pub mod anymap; pub mod anymap;
#[cfg(feature = "std")]
pub mod build_id;
#[cfg(all( #[cfg(all(
any(feature = "cli", feature = "frida_cli", feature = "qemu_cli"), any(feature = "cli", feature = "frida_cli", feature = "qemu_cli"),
feature = "std" feature = "std"

View File

@ -106,7 +106,7 @@ impl EventConfig {
#[must_use] #[must_use]
pub fn from_build_id() -> Self { pub fn from_build_id() -> Self {
EventConfig::BuildID { EventConfig::BuildID {
id: build_id::get(), id: crate::bolts::build_id::get(),
} }
} }

View File

@ -1,4 +1,4 @@
{ {
"B": ["'a'", "'b'"], "B": ["'a'", "'b'"],
"A": ["A 'a'", "'a'"] "A": ["A 'a'", "'a'"]
} }