Skip to content

Commit fc08b21

Browse files
committed
feat: better fuzz
1 parent 63b196e commit fc08b21

File tree

11 files changed

+189
-93
lines changed

11 files changed

+189
-93
lines changed

Cargo.lock

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

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ COPY ./common ./common
1616
COPY ./codegen ./codegen
1717
COPY ./src ./src
1818
COPY ./benches ./benches
19+
COPY ./fuzz ./fuzz
1920

2021
RUN cargo build --release --features $features
2122

common/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ tonic = "0.12.0"
1313
openidconnect = { version = "3.5.0", optional = true }
1414
prost = "0.13.0"
1515
prost-types = "0.13.0"
16-
validator = { version = "0.18.1", features = ["derive"] }
16+
validator = { version = "0.18.1", features = ["derive", "unic"] }
1717

1818
[build-dependencies]
1919
tonic-build = "0.12.0"

common/build.rs

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
3434
"CreatePromptRequest",
3535
r#"#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]"#,
3636
)
37-
.type_attribute(
38-
"CreateFieldRequest",
39-
r#"#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]"#,
40-
)
4137
.type_attribute(
4238
"FieldOptions.options",
4339
r#"#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]"#,
@@ -79,36 +75,58 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
7975
r#"#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]"#,
8076
)
8177
.type_attribute("CreateTargetRequest", "#[derive(validator::Validate)]")
82-
.field_attribute("CreateTargetRequest.name", "#[validate(length(max = 255))]")
78+
.field_attribute(
79+
"CreateTargetRequest.name",
80+
"#[validate(length(min = 1, max = 255), non_control_character)]",
81+
)
82+
.field_attribute(
83+
"CreateTargetRequest.description",
84+
"#[validate(length(min = 1, max = 255), non_control_character)]",
85+
)
8386
.type_attribute("UpdateTargetRequest", "#[derive(validator::Validate)]")
84-
.field_attribute("UpdateTargetRequest.name", "#[validate(length(max = 255))]")
87+
.field_attribute(
88+
"UpdateTargetRequest.name",
89+
"#[validate(length(min = 1, max = 255), non_control_character)]",
90+
)
8591
.field_attribute(
8692
"UpdateTargetRequest.description",
87-
"#[validate(length(max = 255))]",
93+
"#[validate(length(min = 1, max = 255), non_control_character)]",
8894
)
8995
.type_attribute("CreatePromptRequest", "#[derive(validator::Validate)]")
90-
.field_attribute("CreatePromptRequest.title", "#[validate(length(max = 32))]")
96+
.field_attribute(
97+
"CreatePromptRequest.title",
98+
"#[validate(length(min = 1, max = 32), non_control_character)]",
99+
)
91100
.field_attribute(
92101
"UpdatePromptRequest.description",
93-
"#[validate(length(max = 255))]",
102+
"#[validate(length(min = 1, max = 255), non_control_character)]",
94103
)
95104
.type_attribute("UpdatePromptRequest", "#[derive(validator::Validate)]")
96-
.field_attribute("UpdatePromptRequest.title", "#[validate(length(max = 32))]")
105+
.field_attribute(
106+
"UpdatePromptRequest.title",
107+
"#[validate(length(min = 1, max = 32), non_control_character)]",
108+
)
97109
.field_attribute(
98110
"CreatePromptRequest.description",
99-
"#[validate(length(max = 255))]",
111+
"#[validate(length(min = 1, max = 255), non_control_character)]",
100112
)
101113
.type_attribute("CreateFieldRequest", "#[derive(validator::Validate)]")
102-
.field_attribute("CreateFieldRequest.title", "#[validate(length(max = 32))]")
114+
.field_attribute(
115+
"CreateFieldRequest.title",
116+
"#[validate(length(min = 1, max = 32), non_control_character)]",
117+
)
103118
.field_attribute(
104119
"CreateFieldRequest.description",
105-
"#[validate(length(max = 255))]",
120+
"#[validate(length(min = 1, max = 255), non_control_character)]",
106121
)
107122
.type_attribute("UpdateFieldRequest", "#[derive(validator::Validate)]")
108-
.field_attribute("UpdateFieldRequest.title", "#[validate(length(max = 32))]")
123+
.field_attribute(
124+
"UpdateFieldRequest.title",
125+
"#[validate(length(min = 1, max = 32), non_control_character)]",
126+
)
109127
.field_attribute(
110128
"UpdateFieldRequest.description",
111-
"#[validate(length(max = 255))]",
129+
"#[validate(length(min = 1, max = 255), non_control_character)]",
112130
)
113131
.type_attribute(
114132
"GetTargetsRequest",

common/src/tests.rs

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@
2020
//DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2222

23+
use arbitrary::Arbitrary;
2324
use lazy_static::lazy_static;
2425
use openidconnect::{
2526
core::{CoreClient, CoreProviderMetadata},
2627
reqwest::async_http_client,
2728
ClientId, ClientSecret, IssuerUrl, OAuth2TokenResponse, Scope,
2829
};
2930

31+
use crate::proto::{field_options::Options, CreateFieldRequest, FieldType};
32+
3033
lazy_static! {
3134
pub static ref GRPC_ENDPOINT: String = std::env::var("GRPC_ENDPOINT").unwrap();
3235
}
@@ -58,22 +61,55 @@ pub async fn authenticate() -> String {
5861
#[macro_export]
5962
macro_rules! connect {
6063
() => {{
61-
let channel = tonic::transport::Channel::from_static(&$crate::tests::GRPC_ENDPOINT).connect().await.unwrap();
62-
let token: tonic::metadata::MetadataValue<_> = format!("Bearer {}", $crate::tests::authenticate().await).parse().unwrap();
64+
let channel = tonic::transport::Channel::from_static(&$crate::tests::GRPC_ENDPOINT)
65+
.connect()
66+
.await
67+
.unwrap();
68+
let token: tonic::metadata::MetadataValue<_> =
69+
format!("Bearer {}", $crate::tests::authenticate().await)
70+
.parse()
71+
.unwrap();
6372

6473
let client =
65-
$crate::proto::feedback_fusion_v1_client::FeedbackFusionV1Client::with_interceptor(channel, move |mut request: tonic::Request<()>| {
66-
request
67-
.metadata_mut()
68-
.insert("authorization", token.clone());
74+
$crate::proto::feedback_fusion_v1_client::FeedbackFusionV1Client::with_interceptor(
75+
channel,
76+
move |mut request: tonic::Request<()>| {
77+
request
78+
.metadata_mut()
79+
.insert("authorization", token.clone());
6980

70-
Ok(request)
71-
});
81+
Ok(request)
82+
},
83+
);
7284

73-
let public_client = $crate::proto::public_feedback_fusion_v1_client::PublicFeedbackFusionV1Client::connect($crate::tests::GRPC_ENDPOINT.as_str())
85+
let public_client =
86+
$crate::proto::public_feedback_fusion_v1_client::PublicFeedbackFusionV1Client::connect(
87+
$crate::tests::GRPC_ENDPOINT.as_str(),
88+
)
7489
.await
7590
.unwrap();
7691

7792
(client, public_client)
7893
}};
7994
}
95+
96+
impl Arbitrary<'_> for CreateFieldRequest {
97+
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
98+
Ok(CreateFieldRequest {
99+
prompt: "".to_owned(),
100+
title: u
101+
.arbitrary::<String>()?
102+
.chars()
103+
.take(u.int_in_range(0..=34)?)
104+
.collect(),
105+
description: Some(
106+
u.arbitrary::<String>()?
107+
.chars()
108+
.take(u.int_in_range(0..=34)?)
109+
.collect(),
110+
),
111+
field_type: u.int_in_range(0..=6)?,
112+
options: Some(crate::proto::FieldOptions::arbitrary(u)?),
113+
})
114+
}
115+
}

fuzz/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ cargo-fuzz = true
1111
feedback_fusion_common = { path = "../common", features = ["arbitrary"] }
1212

1313
arbitrary = "1.4.1"
14+
lazy_static ="1.5.0"
1415
libfuzzer-sys = "0.4"
15-
serde_yaml = "0.9.34"
1616
tonic = "0.12.0"
1717
tokio = { version = "1.37.0", features = ["full"] }
1818

fuzz/fuzz_targets/fuzz_create_and_export.rs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
#![no_main]
22
use arbitrary::{Arbitrary, Unstructured};
3-
use feedback_fusion_common::proto::{
4-
feedback_fusion_v1_client::FeedbackFusionV1Client, CreateFieldRequest, CreatePromptRequest,
5-
CreateTargetRequest,
6-
};
3+
use feedback_fusion_common::proto::{CreateFieldRequest, CreatePromptRequest, CreateTargetRequest};
4+
use lazy_static::lazy_static;
75
use libfuzzer_sys::fuzz_target;
86

97
#[derive(Debug)]
@@ -25,19 +23,19 @@ struct PromptWithFields {
2523

2624
impl Arbitrary<'_> for FuzzInput {
2725
fn arbitrary(u: &mut Unstructured) -> arbitrary::Result<Self> {
28-
let num_targets = u.int_in_range(0..=10)?;
26+
let num_targets = u.int_in_range(0..=3)?;
2927

3028
let targets = (0..num_targets)
3129
.map(|_| {
3230
let target_request: CreateTargetRequest = u.arbitrary()?;
3331

34-
let num_prompts = u.int_in_range(0..=10)?;
32+
let num_prompts = u.int_in_range(0..=3)?;
3533

3634
let prompts = (0..num_prompts)
3735
.map(|_| {
3836
let prompt_request: CreatePromptRequest = u.arbitrary()?;
3937

40-
let num_fields = u.int_in_range(0..=10)?;
38+
let num_fields = u.int_in_range(0..=4)?;
4139

4240
let fields = (0..num_fields)
4341
.map(|_| u.arbitrary::<CreateFieldRequest>())
@@ -61,33 +59,34 @@ impl Arbitrary<'_> for FuzzInput {
6159
}
6260
}
6361

62+
lazy_static! {
63+
static ref RUNTIME: tokio::runtime::Runtime = tokio::runtime::Runtime::new().unwrap();
64+
}
65+
6466
fuzz_target!(|data: &[u8]| {
6567
let mut u = Unstructured::new(data);
6668

6769
if let Ok(fuzz_input) = FuzzInput::arbitrary(&mut u) {
68-
let runtime = tokio::runtime::Runtime::new().unwrap();
69-
runtime.block_on(async {
70+
RUNTIME.block_on(async {
7071
let (mut client, _) = feedback_fusion_common::connect!();
7172

72-
println!("{:?}", fuzz_input.targets);
73-
7473
for target in fuzz_input.targets {
75-
match client.create_target(target.target_request).await {
74+
match client.create_target(target.target_request.clone()).await {
7675
Ok(target_response) => {
7776
let target_id = target_response.into_inner().id;
7877

7978
for prompt in target.prompts {
80-
let mut prompt_request = prompt.prompt_request;
79+
let mut prompt_request = prompt.prompt_request.clone();
8180
prompt_request.target = target_id.clone();
8281

83-
match client.create_prompt(prompt_request).await {
82+
match client.create_prompt(prompt_request.clone()).await {
8483
Ok(prompt_response) => {
8584
let prompt_id = prompt_response.into_inner().id;
8685

8786
for mut field in prompt.fields {
8887
field.prompt = prompt_id.clone();
8988

90-
if let Err(e) = client.create_field(field).await {
89+
if let Err(e) = client.create_field(field.clone()).await {
9190
handle_grpc_error(e);
9291
}
9392
}

src/database/mssql.sql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'targe
33
BEGIN
44
CREATE TABLE target (
55
id VARCHAR(32) NOT NULL PRIMARY KEY,
6-
name VARCHAR(32) NOT NULL,
6+
name VARCHAR(255) NOT NULL,
77
description VARCHAR(255),
88
updated_at DATETIME,
99
created_at DATETIME
@@ -14,7 +14,7 @@ IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'promp
1414
BEGIN
1515
CREATE TABLE prompt (
1616
id VARCHAR(32) NOT NULL PRIMARY KEY,
17-
title VARCHAR(32) NOT NULL,
17+
title VARCHAR(255) NOT NULL,
1818
description VARCHAR(255) NOT NULL,
1919
target VARCHAR(32) NOT NULL REFERENCES target(id),
2020
active BIT NOT NULL,
@@ -27,7 +27,7 @@ IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'field
2727
BEGIN
2828
CREATE TABLE field (
2929
id VARCHAR(32) NOT NULL PRIMARY KEY,
30-
title VARCHAR(32) NOT NULL,
30+
title VARCHAR(255) NOT NULL,
3131
description VARCHAR(255),
3232
prompt VARCHAR(32) NOT NULL REFERENCES prompt(id),
3333
field_type VARCHAR(32) NOT NULL,

src/database/mysql.sql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
CREATE TABLE IF NOT EXISTS target (
22
id VARCHAR(32) UNIQUE NOT NULL PRIMARY KEY,
3-
name VARCHAR(32) NOT NULL,
3+
name VARCHAR(255) NOT NULL,
44
description VARCHAR(255),
55
updated_at TIMESTAMP(3),
66
created_at TIMESTAMP(3)
77
);
88

99
CREATE TABLE IF NOT EXISTS prompt (
1010
id VARCHAR(32) UNIQUE NOT NULL PRIMARY KEY,
11-
title VARCHAR(32) NOT NULL,
11+
title VARCHAR(255) NOT NULL,
1212
description VARCHAR(255) NOT NULL,
1313
target VARCHAR(32) NOT NULL,
1414
active BOOLEAN NOT NULL,
@@ -19,7 +19,7 @@ CREATE TABLE IF NOT EXISTS prompt (
1919

2020
CREATE TABLE IF NOT EXISTS field (
2121
id VARCHAR(32) UNIQUE NOT NULL PRIMARY KEY,
22-
title VARCHAR(32) NOT NULL,
22+
title VARCHAR(255) NOT NULL,
2323
description VARCHAR(255),
2424
prompt VARCHAR(32) NOT NULL,
2525
field_type VARCHAR(32) NOT NULL,

src/database/postgres.sql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
CREATE TABLE IF NOT EXISTS target (
22
id VARCHAR(32) UNIQUE NOT NULL,
3-
name VARCHAR(32) NOT NULL,
3+
name VARCHAR(255) NOT NULL,
44
description VARCHAR(255),
55
updated_at TIMESTAMP,
66
created_at TIMESTAMP
77
);
88

99
CREATE TABLE IF NOT EXISTS prompt (
1010
id VARCHAR(32) UNIQUE NOT NULL,
11-
title VARCHAR(32) NOT NULL,
11+
title VARCHAR(255) NOT NULL,
1212
description VARCHAR(255) NOT NULL,
1313
target VARCHAR(32) REFERENCES target(id) NOT NULL,
1414
active BOOLEAN NOT NULL,
@@ -18,7 +18,7 @@ CREATE TABLE IF NOT EXISTS prompt (
1818

1919
CREATE TABLE IF NOT EXISTS field (
2020
id VARCHAR(32) UNIQUE NOT NULL,
21-
title VARCHAR(32) NOT NULL,
21+
title VARCHAR(255) NOT NULL,
2222
description VARCHAR(255),
2323
prompt VARCHAR(32) REFERENCES prompt(id) NOT NULL,
2424
field_type VARCHAR(32) NOT NULL,

0 commit comments

Comments
 (0)