Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

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

### Fixes

- Crontab validation when capturing checkins ([#4314](https://github.com/getsentry/sentry-dotnet/pull/4314))

### Dependencies

- Bump Native SDK from v0.9.0 to v0.9.1 ([#4309](https://github.com/getsentry/sentry-dotnet/pull/4309))
Expand Down
4 changes: 2 additions & 2 deletions samples/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<Target Name="GenerateSharedDsnConstant"
BeforeTargets="BeforeCompile"
Condition=" '$(SENTRY_DSN)' != '' and '$(PlatformIsMobile)' == 'true'">
Condition="'$(SENTRY_DSN)' != ''">

<Message Text="Generating shared EnvironmentVariables.g.cs with embedded DSN..." Importance="High" />

Expand All @@ -25,7 +25,7 @@ namespace Sentry.Samples%3B
internal static class EnvironmentVariables
{
/// &lt;summary&gt;
/// To make things easier for the SDK maintainers we have a custom build target that writes the
/// To make things easier for the SDK maintainers, we have a custom build target that writes the
/// SENTRY_DSN environment variable into an EnvironmentVariables class that is available for mobile
/// targets. This allows us to share one DSN defined in the ENV across desktop and mobile samples.
/// Generally, you won't want to do this in your own mobile projects though - you should set the DSN
Expand Down
28 changes: 20 additions & 8 deletions src/Sentry/SentryMonitorOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,26 @@ public enum SentryMonitorInterval
/// </summary>
public partial class SentryMonitorOptions : ISentryJsonSerializable
{
// Breakdown of the validation
// ^(\*|([0-5]?\d)) Minute 0 - 59
// (\s+)(\*|([01]?\d|2[0-3])) Hour 0 - 23
// (\s+)(\*|([1-9]|[12]\d|3[01])) Day 1 - 31
// (\s+)(\*|([1-9]|1[0-2])) Month 1 - 12
// (\s+)(\*|([0-7])) Weekday 0 - 7
// $ End of string
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]))$";
// Breakdown of the validation regex pattern:
// For each time field (minute, hour, day, month, weekday):
// - Allows * for "any value"
// - Allows */n for step values where n must be:
// - Minutes: 1-59
// - Hours: 1-23
// - Days: 1-31
// - Months: 1-12
// - Weekdays: 1-7
// - Allows single values within their valid ranges
// - Allows ranges (e.g., 8-10)
// - Allows lists of values and ranges (e.g., 6,8,9 or 8-10,12-14)
//
// Valid ranges for each field:
// - Minutes: 0-59
// - Hours: 0-23
// - Days: 1-31
// - Months: 1-12
// - Weekdays: 0-7 (0 and 7 both represent Sunday)
private const string ValidCrontabPattern = @"^(\*|(\*\/([1-9]|[1-5][0-9]))|([0-5]?\d)(-[0-5]?\d)?)(,([0-5]?\d)(-[0-5]?\d)?)*(\s+)(\*|(\*\/([1-9]|1[0-9]|2[0-3]))|([01]?\d|2[0-3])(-([01]?\d|2[0-3]))?)((,([01]?\d|2[0-3])(-([01]?\d|2[0-3]))?)*)?(\s+)(\*|(\*\/([1-9]|[12][0-9]|3[01]))|([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]|1[0-2]))|([1-9]|1[0-2])(-([1-9]|1[0-2]))?)((,([1-9]|1[0-2])(-([1-9]|1[0-2]))?)*)?(\s+)(\*|(\*\/[1-7])|[0-7](-[0-7])?)((,[0-7](-[0-7])?)*)?$";

private SentryMonitorScheduleType _type = SentryMonitorScheduleType.None;
private string? _crontab;
Expand Down
112 changes: 112 additions & 0 deletions test/Sentry.Tests/SentryMonitorOptionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
namespace Sentry.Tests;

public class SentryMonitorOptionsTests
{
/*
The time and date fields are:
field allowed values
----- --------------
minute 0-59
hour 0-23
day of month 1-31
month 1-12 (or names, see below)
day of week 0-7 (0 or 7 is Sunday, or use names)

Special characters are:
* any value
, value list separator
- range of values
/ step values
*/
[Theory]
[InlineData("* * * * *")]
[InlineData("0 0 1 1 *")]
[InlineData("0 0 1 * 0")]
[InlineData("59 23 31 12 7")]
[InlineData("0 */2 * * *")]
[InlineData("0 8-10 * * *")]
[InlineData("0 6,8,9 * * *")]
// Step values (*/n)
[InlineData("*/15 * * * *")] // Every 15 minutes
[InlineData("0 */6 * * *")] // Every 6 hours
[InlineData("0 0 */2 * *")] // Every 2 days
[InlineData("0 0 1 */3 *")] // Every 3 months
// Complex ranges
[InlineData("1-15 * * * *")] // Minutes 1 through 15
[InlineData("* 9-17 * * *")] // Business hours
[InlineData("* * 1-15,16-31 * *")] // Split day ranges
// Multiple comma-separated values
[InlineData("1,15,30,45 * * * *")] // Specific minutes
[InlineData("* 9,12,15,17 * * *")] // Specific hours
[InlineData("0 0 * * 1,3,5")] // Monday, Wednesday, Friday
// Combinations of special characters
[InlineData("*/15 9-17 * * 1-5")] // Every 15 min during business hours on weekdays
[InlineData("0 8-10,13-15 * * *")] // Morning and afternoon ranges
// Edge cases
[InlineData("0 0 1 1 0")] // Minimum values
[InlineData("*/1 * * * *")] // Step of 1
[InlineData("* * 31 */2 *")] // 31st of every other month
public void Interval_ValidCrontab_DoesNotThrow(string crontab)
{
// Arrange
var options = new SentryMonitorOptions();

// Act
options.Interval(crontab);
}

[Fact]
public void Interval_SetMoreThanOnce_Throws()
{
// Arrange
var options = new SentryMonitorOptions();

// Act
options.Interval(1, SentryMonitorInterval.Month);
Assert.Throws<ArgumentException>(() => options.Interval(2, SentryMonitorInterval.Day));
}

[Theory]
[InlineData("")]
[InlineData("not a crontab")]
[InlineData("* * a * *")]
[InlineData("60 * * * *")]
[InlineData("* 24 * * *")]
[InlineData("* * 32 * *")]
[InlineData("* * * 13 *")]
[InlineData("* * * * 8")]
// Invalid step values
[InlineData("*/0 * * * *")] // Step value cannot be 0
[InlineData("*/100 * * * *")] // Step value too large for minutes
[InlineData("* */25 * * *")] // Step value too large for hours
[InlineData("* * */32 * *")] // Step value too large for days
[InlineData("* * * */13 *")] // Step value too large for months
[InlineData("* * * * */8")] // Step value too large for weekdays
[InlineData("*/60 * * * *")] // Step value equals max value for minutes
[InlineData("* */24 * * *")] // Step value equals max value for hours
// Invalid ranges
[InlineData("5-60 * * * *")] // Minute range exceeds 59
[InlineData("* 5-24 * * *")] // Hour range exceeds 23
[InlineData("* * 0-31 * *")] // Day cannot be 0
[InlineData("* * * 0-12 *")] // Month cannot be 0
// Invalid combinations
[InlineData("*-5 * * * *")] // Invalid range with asterisk
[InlineData("1,2,60 * * * *")] // Invalid value in list
[InlineData("1-5-10 * * * *")] // Multiple ranges
[InlineData("*/2/3 * * * *")] // Multiple steps
// Malformed expressions
[InlineData("* * * *")] // Too few fields
[InlineData("* * * * * *")] // Too many fields
[InlineData("** * * * *")] // Double asterisk
[InlineData("*/* * * * *")] // Invalid step format
[InlineData(",1,2 * * * *")] // Leading comma
[InlineData("1,2, * * * *")] // Trailing comma
public void CaptureCheckIn_InvalidCrontabSet_Throws(string crontab)
{
// Arrange
var options = new SentryMonitorOptions();

// Act
Assert.Throws<ArgumentException>(() => options.Interval(crontab));
}
}
Loading