DEV Community

Mario Alberto Arce
Mario Alberto Arce

Posted on

Stop Rewriting `IsWeekend()`

I've written this code more times than I can count:

var isWeekend = date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday;

var isEmpty = list == null || list.Count == 0;

string json;
try 
{ 
     json = JsonConvert.SerializeObject(user); 
}
catch 
{
     json = "{}"; 
}
Enter fullscreen mode Exit fullscreen mode

And the PowerCSharp version of the same three things:

var isWeekend = date.IsWeekend();
var isEmpty = list.IsNullOrEmpty();
var json = JsonHelper.SafeSerialize(user); // never throws, "{}" on failure
Enter fullscreen mode Exit fullscreen mode

None of that is clever. That's the point — it's boilerplate, and boilerplate is exactly what's worth centralizing once you've written it in enough different projects.

The one in this set that's actually worth pausing on is path handling:

// Vulnerable — fileName = "../../etc/passwd" escapes uploadDir
var fullPath = Path.Combine(uploadDir, fileName);

// PowerCSharp
try
{
    var safePath = PathExtensions.CombineAndValidate(uploadDir, fileName);
    SaveFile(safePath, content);
}
catch (SecurityException)
{
    // reject, log, return 403
}
Enter fullscreen mode Exit fullscreen mode

CombineAndValidate canonicalizes the combined path and checks the result is still inside uploadDir before handing it back — the same base-directory containment check Veracode recommends for CWE-73 (path traversal). It's one method doing one job; if your threat model needs more than path containment, that's a separate conversation, and I'd rather say that up front than let anyone assume this is a full defense-in-depth stack.

Also in the Extensions package: runtime LINQ from strings.

var predicate = "Age > 18 && Name.Contains('John')".GetExpressionDelegate<Person>();
var filtered = people.Where(predicate);
Enter fullscreen mode Exit fullscreen mode

Genuinely useful for search boxes and query-parameter filters. Also genuinely an injection surface — a string like "Age > 0 || 1 == 1" bypasses whatever filter you meant to apply. Treat it like SQL built from user input: allow-list the properties and operators you accept before the string reaches GetExpressionDelegate. PowerCSharp gives you the parsing, not a validation layer — I'd rather be explicit about that than have someone assume otherwise and find out the hard way.

Package map, so you know what to actually install:

Package What it's for Reach for it when
PowerCSharp.Core Shared interfaces, no logic You're using any other package (it's the dependency)
PowerCSharp.Extensions 100+ extension methods (string/DateTime/collections/LINQ/HTTP/JSON) Almost always
PowerCSharp.Extensions.AspNetCore ASP.NET Core-specific extensions (Uri.AddParameter, config binding) You're in an ASP.NET Core project
PowerCSharp.Utilities FileHelper, MathHelper File I/O or math edge cases
PowerCSharp.Helpers JsonHelper, CryptoHelper, EnvironmentHelper Safe JSON/hashing/env access
PowerCSharp.Compatibility .NET Framework bridges (AsyncHelper.RunSync, ValidationHelper) You're still on .NET Framework/System.Web

Install only what you need — they're independently versioned on purpose.


PowerCSharp

Top comments (0)