Skip to content

Commit 6da328b

Browse files
fix: Crontab validation when capturing checkins (#4314)
Resolves #3719: - #3719
1 parent dc2b326 commit 6da328b

File tree

3 files changed

+131
-8
lines changed

3 files changed

+131
-8
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
- Added StartSpan and GetTransaction methods to the SentrySdk ([#4303](https://github.com/getsentry/sentry-dotnet/pull/4303))
88

9+
### Fixes
10+
11+
- Crontab validation when capturing checkins ([#4314](https://github.com/getsentry/sentry-dotnet/pull/4314))
12+
913
### Dependencies
1014

1115
- Bump Native SDK from v0.9.0 to v0.9.1 ([#4309](https://github.com/getsentry/sentry-dotnet/pull/4309))

src/Sentry/SentryMonitorOptions.cs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,21 @@ public enum SentryMonitorInterval
5151
/// </summary>
5252
public partial class SentryMonitorOptions : ISentryJsonSerializable
5353
{
54-
// Breakdown of the validation
55-
// ^(\*|([0-5]?\d)) Minute 0 - 59
56-
// (\s+)(\*|([01]?\d|2[0-3])) Hour 0 - 23
57-
// (\s+)(\*|([1-9]|[12]\d|3[01])) Day 1 - 31
58-
// (\s+)(\*|([1-9]|1[0-2])) Month 1 - 12
59-
// (\s+)(\*|([0-7])) Weekday 0 - 7
60-
// $ End of string
61-
private const string ValidCrontabPattern = @"^(\*|([0-5]?\d))(\s+)(\*|([01]?\d|2[0-3]))(\s+)(\*|([1-9]|[12]\d|3[01]))(\s+)(\*|([1-9]|1[0-2]))(\s+)(\*|([0-7]))$";
54+
// Breakdown of the validation regex pattern:
55+
// For each time field (minute, hour, day, month, weekday):
56+
// - Allows * for "any value"
57+
// - Allows */n for step values where n must be any positive integer (except zero)
58+
// - Allows single values within their valid ranges
59+
// - Allows ranges (e.g., 8-10)
60+
// - Allows lists of values and ranges (e.g., 6,8,9 or 8-10,12-14)
61+
//
62+
// Valid ranges for each field:
63+
// - Minutes: 0-59
64+
// - Hours: 0-23
65+
// - Days: 1-31
66+
// - Months: 1-12
67+
// - Weekdays: 0-7 (0 and 7 both represent Sunday)
68+
private const string ValidCrontabPattern = @"^(\*|(\*\/([1-9][0-9]*))|([0-5]?\d)(-[0-5]?\d)?)(,([0-5]?\d)(-[0-5]?\d)?)*(\s+)(\*|(\*\/([1-9][0-9]*))|([01]?\d|2[0-3])(-([01]?\d|2[0-3]))?)((,([01]?\d|2[0-3])(-([01]?\d|2[0-3]))?)*)?(\s+)(\*|(\*\/([1-9][0-9]*))|([1-9]|[12]\d|3[01])(-([1-9]|[12]\d|3[01]))?)((,([1-9]|[12]\d|3[01])(-([1-9]|[12]\d|3[01]))?)*)?(\s+)(\*|(\*\/([1-9][0-9]*))|([1-9]|1[0-2])(-([1-9]|1[0-2]))?)((,([1-9]|1[0-2])(-([1-9]|1[0-2]))?)*)?(\s+)(\*|(\*\/([1-9][0-9]*))|[0-7](-[0-7])?)((,[0-7](-[0-7])?)*)?$";
6269

6370
private SentryMonitorScheduleType _type = SentryMonitorScheduleType.None;
6471
private string? _crontab;
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
namespace Sentry.Tests;
2+
3+
public class SentryMonitorOptionsTests
4+
{
5+
/*
6+
The time and date fields are:
7+
field allowed values
8+
----- --------------
9+
minute 0-59
10+
hour 0-23
11+
day of month 1-31
12+
month 1-12 (or names, see below)
13+
day of week 0-7 (0 or 7 is Sunday, or use names)
14+
15+
Special characters are:
16+
* any value
17+
, value list separator
18+
- range of values
19+
/ step values
20+
*/
21+
[Theory]
22+
[InlineData("* * * * *")]
23+
[InlineData("0 0 1 1 *")]
24+
[InlineData("0 0 1 * 0")]
25+
[InlineData("59 23 31 12 7")]
26+
[InlineData("0 */2 * * *")]
27+
[InlineData("0 8-10 * * *")]
28+
[InlineData("0 6,8,9 * * *")]
29+
// Step values (*/n)
30+
[InlineData("*/15 * * * *")] // Every 15 minutes
31+
[InlineData("0 */6 * * *")] // Every 6 hours
32+
[InlineData("0 0 */2 * *")] // Every 2 days
33+
[InlineData("0 0 1 */3 *")] // Every 3 months
34+
[InlineData("*/100 * * * *")] // Step value 100 for minutes
35+
[InlineData("* */25 * * *")] // Step value 25 for hours
36+
[InlineData("* * */32 * *")] // Step value 32 for days
37+
[InlineData("* * * */13 *")] // Step value 13 for months
38+
[InlineData("* * * * */8")] // Step value 8 for weekdays
39+
[InlineData("*/60 * * * *")] // Step value 60 for minutes
40+
[InlineData("* */24 * * *")] // Step value 24 for hours
41+
// Complex ranges
42+
[InlineData("1-15 * * * *")] // Minutes 1 through 15
43+
[InlineData("* 9-17 * * *")] // Business hours
44+
[InlineData("* * 1-15,16-31 * *")] // Split day ranges
45+
// Multiple comma-separated values
46+
[InlineData("1,15,30,45 * * * *")] // Specific minutes
47+
[InlineData("* 9,12,15,17 * * *")] // Specific hours
48+
[InlineData("0 0 * * 1,3,5")] // Monday, Wednesday, Friday
49+
// Combinations of special characters
50+
[InlineData("*/15 9-17 * * 1-5")] // Every 15 min during business hours on weekdays
51+
[InlineData("0 8-10,13-15 * * *")] // Morning and afternoon ranges
52+
// Edge cases
53+
[InlineData("0 0 1 1 0")] // Minimum values
54+
[InlineData("*/1 * * * *")] // Step of 1
55+
[InlineData("* * 31 */2 *")] // 31st of every other month
56+
public void Interval_ValidCrontab_DoesNotThrow(string crontab)
57+
{
58+
// Arrange
59+
var options = new SentryMonitorOptions();
60+
61+
// Act
62+
options.Interval(crontab);
63+
}
64+
65+
[Fact]
66+
public void Interval_SetMoreThanOnce_Throws()
67+
{
68+
// Arrange
69+
var options = new SentryMonitorOptions();
70+
71+
// Act
72+
options.Interval(1, SentryMonitorInterval.Month);
73+
Assert.Throws<ArgumentException>(() => options.Interval(2, SentryMonitorInterval.Day));
74+
}
75+
76+
[Theory]
77+
[InlineData("")]
78+
[InlineData("not a crontab")]
79+
[InlineData("* * a * *")]
80+
[InlineData("60 * * * *")]
81+
[InlineData("* 24 * * *")]
82+
[InlineData("* * 32 * *")]
83+
[InlineData("* * * 13 *")]
84+
[InlineData("* * * * 8")]
85+
// Invalid step values
86+
[InlineData("*/0 * * * *")] // Step value cannot be 0
87+
// Invalid ranges
88+
[InlineData("5-60 * * * *")] // Minute range exceeds 59
89+
[InlineData("* 5-24 * * *")] // Hour range exceeds 23
90+
[InlineData("* * 0-31 * *")] // Day cannot be 0
91+
[InlineData("* * * 0-12 *")] // Month cannot be 0
92+
// Invalid combinations
93+
[InlineData("*-5 * * * *")] // Invalid range with asterisk
94+
[InlineData("1,2,60 * * * *")] // Invalid value in list
95+
[InlineData("1-5-10 * * * *")] // Multiple ranges
96+
[InlineData("*/2/3 * * * *")] // Multiple steps
97+
// Malformed expressions
98+
[InlineData("* * * *")] // Too few fields
99+
[InlineData("* * * * * *")] // Too many fields
100+
[InlineData("** * * * *")] // Double asterisk
101+
[InlineData("*/* * * * *")] // Invalid step format
102+
[InlineData(",1,2 * * * *")] // Leading comma
103+
[InlineData("1,2, * * * *")] // Trailing comma
104+
public void CaptureCheckIn_InvalidCrontabSet_Throws(string crontab)
105+
{
106+
// Arrange
107+
var options = new SentryMonitorOptions();
108+
109+
// Act
110+
Assert.Throws<ArgumentException>(() => options.Interval(crontab));
111+
}
112+
}

0 commit comments

Comments
 (0)