• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

divviup / divviup-api / 24355408539

13 Apr 2026 04:46PM UTC coverage: 56.907% (+0.004%) from 56.903%
24355408539

push

github

web-flow
Add max-length validation on string input fields (#2192)

Add upper bounds to name fields (max 255), API URLs (max 2048),
and bearer tokens (max 4096) across aggregator and task creation
and update endpoints. Previously only minimum length was enforced.

2 of 3 new or added lines in 1 file covered. (66.67%)

2 existing lines in 1 file now uncovered.

4074 of 7159 relevant lines covered (56.91%)

59.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.67
/src/entity/task/update_task.rs
1
use janus_messages::Time as JanusTime;
2
use sea_orm::{ActiveModelTrait, ActiveValue, IntoActiveModel};
3
use serde::Deserialize;
4
use time::OffsetDateTime;
5
use tokio::try_join;
6
use trillium_client::Client;
7
use validator::{Validate, ValidationError, ValidationErrors};
8

9
use crate::{deserialize_some, entity::Aggregator, handler::Error, Crypter, Db};
10

11
use super::assert_same;
12

13
#[derive(Default, Deserialize, Validate, Debug)]
14
pub struct UpdateTask {
15
    #[validate(custom(function = "validate_name"))]
16
    name: Option<String>,
17
    #[serde(default, deserialize_with = "deserialize_some")]
18
    expiration: Option<Expiration>,
19
}
20

21
#[derive(Debug, Deserialize, Clone, Copy)]
22
struct Expiration(#[serde(default, with = "time::serde::rfc3339::option")] Option<OffsetDateTime>);
23

24
fn validate_name(name: &str) -> Result<(), ValidationError> {
7✔
25
    if name.is_empty() {
7✔
26
        return Err(ValidationError::new("name-too-short"));
1✔
27
    }
6✔
28
    if name.len() > 255 {
6✔
NEW
29
        return Err(ValidationError::new("name-too-long"));
×
30
    }
6✔
31
    Ok(())
6✔
32
}
7✔
33

34
impl UpdateTask {
35
    pub async fn update_aggregator_expiration(
28✔
36
        &self,
28✔
37
        aggregator: Aggregator,
28✔
38
        task_id: &str,
28✔
39
        http_client: &Client,
28✔
40
        crypter: &Crypter,
28✔
41
    ) -> Result<(), Error> {
28✔
42
        let expiration = self
27✔
43
            .expiration
27✔
44
            .as_ref()
27✔
45
            .unwrap()
27✔
46
            .0
27✔
47
            .map(|expiration| {
27✔
48
                let seconds: u64 = expiration.unix_timestamp().try_into().map_err(|_| {
23✔
49
                    let mut ve = ValidationErrors::new();
1✔
50
                    ve.add("expiration", ValidationError::new("pre-epoch-timestamp"));
1✔
51
                    Error::from(ve)
1✔
52
                })?;
1✔
53
                Ok::<_, Error>(JanusTime::from_seconds_since_epoch(seconds))
22✔
54
            })
23✔
55
            .transpose()?;
27✔
56
        let response = aggregator
26✔
57
            .client(http_client.clone(), crypter)?
26✔
58
            .update_task_expiration(task_id, expiration)
26✔
59
            .await?;
26✔
60
        assert_same(expiration, response.task_expiration, "expiration")?;
24✔
61
        Ok(())
24✔
62
    }
27✔
63

64
    /// Validates the request. Updates task definitions in the aggregators, if necessary. Returns
65
    /// an [`ActiveModel`] for committing to the database.
66
    pub async fn update(
12✔
67
        self,
12✔
68
        http_client: &Client,
12✔
69
        db: &Db,
12✔
70
        crypter: &Crypter,
12✔
71
        model: super::Model,
12✔
72
    ) -> Result<super::ActiveModel, Error> {
12✔
73
        self.validate()?;
12✔
74
        let mut am = model.clone().into_active_model();
11✔
75
        if let Some(ref name) = self.name {
11✔
76
            am.name = ActiveValue::Set(name.clone());
6✔
77
        }
6✔
78
        if let Some(ref expiration) = self.expiration {
11✔
79
            try_join!(
6✔
80
                self.update_aggregator_expiration(
6✔
81
                    model.leader_aggregator(db).await?,
6✔
82
                    &model.id,
6✔
83
                    http_client,
6✔
84
                    crypter
6✔
85
                ),
86
                self.update_aggregator_expiration(
6✔
87
                    model.helper_aggregator(db).await?,
6✔
88
                    &model.id,
6✔
89
                    http_client,
6✔
90
                    crypter
6✔
91
                )
92
            )?;
1✔
93
            am.expiration = ActiveValue::set(expiration.0);
5✔
94
        }
5✔
95
        if am.is_changed() {
10✔
96
            am.updated_at = ActiveValue::Set(OffsetDateTime::now_utc());
10✔
97
        }
10✔
98
        Ok(am)
10✔
99
    }
12✔
100

101
    pub fn expiration(expiration: Option<OffsetDateTime>) -> Self {
8✔
102
        Self {
8✔
103
            expiration: Some(Expiration(expiration)),
8✔
104
            ..Default::default()
8✔
105
        }
8✔
106
    }
8✔
107
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc