
Research
/Security News
Malicious npm Packages Target WhatsApp Developers with Remote Kill Switch
Two npm packages masquerading as WhatsApp developer libraries include a kill switch that deletes all files if the phone number isn’t whitelisted.
A comprehensive .NET utility library providing extensive type conversion extensions, string manipulation utilities, date/time helpers, file system operations, collection extensions, and various helper classes for everyday development tasks. Supports multiple .NET frameworks including .NET Standard 2.0, .NET Framework 4.7.2, .NET 8.0 and .NET 9.0.
A comprehensive .NET utility library providing extensive type conversion extensions, string manipulation utilities, date/time helpers, file system operations, collection extensions, and various helper classes for everyday development tasks.
Linger.Utils offer a rich collection of extension methods and helper classes that make common programming tasks simpler and more efficient. The library follows modern C# coding practices and supports multiple .NET framework versions.
dotnet add package Linger.Utils
using Linger.Extensions.Core;
// String validation
string email = "user@example.com";
bool isValid = email.IsEmail();
// String conversion
string number = "123";
int result = number.ToInt(0); // Returns 123, or 0 if conversion fails
int? nullableResult = number.ToIntOrNull(); // Returns nullable type
// String manipulation
string text = " Hello World ";
string cleaned = text.Trim(); // Removes whitespace from both ends (.NET native method)
// String extraction
string longText = "Hello World";
string leftPart = longText.Left(5); // Get left 5 characters: Hello
string rightPart = longText.Right(5); // Get right 5 characters: World
string part = longText.SafeSubstring(0, 20); // Won't throw if length exceeds
// String checks
bool isEmpty = text.IsNullOrEmpty();
bool isNumber = number.IsNumber(); // Check if it's a number
bool isInt = number.IsInteger(); // Check if it's an integer
using Linger.Extensions.Core;
DateTime date = DateTime.Now;
// Age calculation
DateTime birthDate = new DateTime(1990, 5, 15);
int age = birthDate.CalculateAge();
// Date range operations
bool isInRange = date.InRange(DateTime.Today, DateTime.Today.AddDays(7));
// Date operations
DateTime startOfDay = date.StartOfDay(); // Beginning of the day
DateTime endOfDay = date.EndOfDay(); // End of the day
DateTime startOfMonth = date.StartOfMonth(); // Beginning of the month
DateTime endOfMonth = date.EndOfMonth(); // End of the month
using Linger.Helper;
// File operations
FileHelper.WriteText("data.txt", "Hello World");
string content = FileHelper.ReadText("data.txt");
// File copy with directory creation
FileHelper.CopyFile("source.txt", "backup/dest.txt");
// Safe file deletion
FileHelper.DeleteFileIfExists("temp.txt");
// Directory operations
FileHelper.EnsureDirectoryExists("logs/2024");
using Linger.Extensions.Collection;
var list = new List<int> { 1, 2, 3, 4, 5 };
// Safe collection state checking
bool isEmpty = list.IsNullOrEmpty(); // Check if null or empty
// Pagination
var pagedResult = list.Paging(2, 2); // Page 2, 2 items per page: [3, 4]
// Convert to delimited string
string result = list.ToSeparatedString(", "); // "1, 2, 3, 4, 5"
// Execute action on each element
list.ForEach(Console.WriteLine); // Print each element
// Convert to DataTable
var dataTable = list.Select(x => new { Value = x }).ToDataTable();
using Linger.Extensions.Core;
// Null-safe operations
object obj = GetSomeObject();
string result = obj.ToSafeString("default");
// Type checking
string stringValue = obj.ToString(); // .NET native method
bool isNumber = stringValue.IsNumber();
bool isInt = stringValue.IsInteger();
bool isDouble = stringValue.IsDouble();
// Object conversion
var stringRepresentation = obj.ToStringOrNull();
// Range checking (for numeric values)
int value = 5;
bool inRange = value.InRange(1, 10); // Check if in range
using Linger.Extensions;
// Object to JSON
var user = new { Name = "John", Age = 30 };
string json = user.ToJsonString(); // or user.SerializeJson()
// JSON to object
var userObj = json.Deserialize<User>(); // or json.DeserializeJson<User>()
// Dynamic JSON object
dynamic dynamicObj = json.DeserializeDynamicJsonObject();
string name = dynamicObj.Name; // Access properties
// JSON to DataTable (string extension)
string jsonArray = "[{\"Name\":\"John\",\"Age\":30}]";
DataTable? dataTable = jsonArray.ToDataTable();
using Linger.Extensions.Core;
// GUID checking
Guid guid = Guid.NewGuid();
bool isEmpty = guid.IsEmpty(); // Check if empty GUID
bool isNotEmpty = guid.IsNotEmpty(); // Check if not empty
// Nullable GUID operations
Guid? nullableGuid = null;
bool isNull = nullableGuid.IsNull(); // Check if null
bool isNotNull = nullableGuid.IsNotNull(); // Check if not null
bool isNullOrEmpty = nullableGuid.IsNullOrEmpty(); // Check if null or empty
bool isNotNullAndEmpty = nullableGuid.IsNotNullAndEmpty(); // Check if neither null nor empty
// GUID conversion
long longValue = guid.ToInt64(); // Convert to Int64
int intValue = guid.ToInt32(); // Convert to Int32
// .NET 9+ feature: V7 GUID timestamp extraction
#if NET9_0_OR_GREATER
DateTimeOffset timestamp = guid.GetTimestamp(); // Only for V7 GUIDs
#endif
using Linger.Extensions.Core;
int[] numbers = { 1, 2, 3, 4, 5 };
// Execute action on each element
numbers.ForEach(n => Console.WriteLine(n)); // Output: 1 2 3 4 5
// Iterate with index
numbers.ForEach((n, index) => Console.WriteLine($"Index {index}: {n}"));
// Output: Index 0: 1, Index 1: 2, ...
using Linger.Extensions.Core;
public enum Status
{
Active = 1,
Inactive = 2,
Pending = 3
}
// String to enum
string statusName = "Active";
Status status = statusName.GetEnum<Status>(); // or statusName.ToEnum<Status>()
// Integer to enum
int statusValue = 1;
Status statusFromInt = statusValue.GetEnum<Status>();
// Get enum name
string enumName = statusValue.GetEnumName<Status>(); // Returns "Active"
// Get enum description (if Description attribute exists)
string description = status.GetDescription(); // Get description text
using Linger.Helper;
public void ProcessData(string data, IEnumerable<int> numbers, string filePath)
{
// Basic validation
data.EnsureIsNotNull(nameof(data)); // Ensure not null
data.EnsureIsNotNullAndEmpty(nameof(data)); // Ensure not null or empty
data.EnsureIsNotNullAndWhiteSpace(nameof(data)); // Ensure not null, empty or whitespace
// Collection validation
numbers.EnsureIsNotNullOrEmpty(nameof(numbers)); // Ensure collection is not null or empty
// File system validation
filePath.EnsureFileExist(nameof(filePath)); // Ensure file exists
Path.GetDirectoryName(filePath).EnsureDirectoryExist(); // Ensure directory exists
// Condition validation
(data.Length > 0).EnsureIsTrue(nameof(data), "Data must not be empty");
(numbers.Count() < 1000).EnsureIsTrue(nameof(numbers), "Too many items");
// Range validation
int value = 5;
value.EnsureIsInRange(1, 10, nameof(value)); // Ensure value is in range
// Null checking
object? obj = GetSomeObject();
obj.EnsureIsNotNull(nameof(obj)); // If object should not be null
// or
obj.EnsureIsNull(nameof(obj)); // If object should be null
}
using Linger.Helper;
// Retry operation with configurable policy
var options = new RetryOptions
{
MaxRetries = 3,
BaseDelayMs = 1000 // 1 second
};
var retryHelper = new RetryHelper(options);
var result = await retryHelper.ExecuteAsync(
async () => await SomeOperationThatMightFail(),
"Operation Name"
);
// Or use default options
var defaultRetryHelper = new RetryHelper();
var result2 = await defaultRetryHelper.ExecuteAsync(
async () => await AnotherOperationThatMightFail(),
"Another Operation Name"
);
using Linger.Helper;
using Linger.Enums;
// Dynamic expression building
// Basic expressions
Expression<Func<User, bool>> trueExpression = ExpressionHelper.True<User>();
Expression<Func<User, bool>> falseExpression = ExpressionHelper.False<User>();
// Single condition expressions
Expression<Func<User, bool>> ageFilter = ExpressionHelper.CreateGreaterThan<User>("Age", "18");
Expression<Func<User, bool>> nameFilter = ExpressionHelper.GetContains<User>("Name", "John");
// Build complex expressions using condition collections
var conditions = new List<Condition>
{
new Condition { Field = "Age", Op = CompareOperator.GreaterThan, Value = 18 },
new Condition { Field = "Name", Op = CompareOperator.Contains, Value = "John" }
};
Expression<Func<User, bool>> complexFilter = ExpressionHelper.BuildLambda<User>(conditions);
using Linger.Helper.PathHelpers;
// Path normalization - handles relative paths, duplicate separators, etc.
string messyPath = @"C:\temp\..\folder\.\file.txt";
string normalized = StandardPathHelper.NormalizePath(messyPath);
// Result: "C:\folder\file.txt" (Windows) or "/folder/file.txt" (Unix)
// Path comparison - cross-platform safe path equality check
string path1 = @"C:\Users\Documents\file.txt";
string path2 = @"c:\users\documents\FILE.TXT"; // Different case
bool pathEquals = StandardPathHelper.PathEquals(path1, path2); // Windows: true, Linux: false
// Get relative path - from base path to target path
string basePath = @"C:\Projects\MyApp";
string targetPath = @"C:\Projects\MyApp\src\Components\Button.cs";
string relative = StandardPathHelper.GetRelativePath(basePath, targetPath);
// Result: "src\Components\Button.cs" (Windows) or "src/Components/Button.cs" (Unix)
// Resolve absolute path - convert relative path to absolute
string workingDir = @"C:\Projects";
string relativePath = @"MyApp\src\file.txt";
string absolutePath = StandardPathHelper.ResolveToAbsolutePath(workingDir, relativePath);
// Result: "C:\Projects\MyApp\src\file.txt"
// Check for invalid path characters
string suspiciousPath = "file<name>.txt"; // Contains invalid character '<'
bool hasInvalidChars = StandardPathHelper.ContainsInvalidPathChars(suspiciousPath); // true
// Check if file or directory exists
string filePath = @"C:\temp\data.txt";
bool fileExists = StandardPathHelper.Exists(filePath, checkAsFile: true); // Check as file
bool dirExists = StandardPathHelper.Exists(filePath, checkAsFile: false); // Check as directory
// Get parent directory path
string deepPath = @"C:\Projects\MyApp\src\Components\Button.cs";
string parentDir = StandardPathHelper.GetParentDirectory(deepPath, levels: 1);
// Result: "C:\Projects\MyApp\src\Components"
string grandParentDir = StandardPathHelper.GetParentDirectory(deepPath, levels: 2);
// Result: "C:\Projects\MyApp\src"
ToIntOrNull()
over ToInt()
when conversion might failIsNullOrEmpty()
for validationGuardExtensions
methods like EnsureIsNotNull()
, EnsureIsNotNullAndEmpty()
for input validationusing
statements for disposable resourcesIsEmpty()
and IsNotEmpty()
instead of direct comparisonForEach()
extension methods to simplify array and collection iterationThe library has minimal external dependencies:
Contributions are welcome! Please feel free to submit a Pull Request. Please ensure:
This project is licensed under the terms of the license provided with the Linger project.
For more information about the Linger framework and other related packages, visit the Linger Project Repository.
FAQs
A comprehensive .NET utility library providing extensive type conversion extensions, string manipulation utilities, date/time helpers, file system operations, collection extensions, and various helper classes for everyday development tasks. Supports multiple .NET frameworks including .NET Standard 2.0, .NET Framework 4.7.2, .NET 8.0 and .NET 9.0.
We found that linger.utils demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
/Security News
Two npm packages masquerading as WhatsApp developer libraries include a kill switch that deletes all files if the phone number isn’t whitelisted.
Research
/Security News
Socket uncovered 11 malicious Go packages using obfuscated loaders to fetch and execute second-stage payloads via C2 domains.
Security News
TC39 advances 11 JavaScript proposals, with two moving to Stage 4, bringing better math, binary APIs, and more features one step closer to the ECMAScript spec.