Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
23 changes: 15 additions & 8 deletions src/Sentry/SentryMonitorOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,21 @@ 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 any positive integer (except zero)
// - 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][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])?)*)?$";

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
[InlineData("*/100 * * * *")] // Step value 100 for minutes
[InlineData("* */25 * * *")] // Step value 25 for hours
[InlineData("* * */32 * *")] // Step value 32 for days
[InlineData("* * * */13 *")] // Step value 13 for months
[InlineData("* * * * */8")] // Step value 8 for weekdays
[InlineData("*/60 * * * *")] // Step value 60 for minutes
[InlineData("* */24 * * *")] // Step value 24 for hours
// 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
// 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