Files
sbox-public/engine/Sandbox.Compiling/Compiler/Blacklist/BlacklistCodeWalker.cs
Matt Stevens 3f11417817 Whitelist: explicit Unsafe/Span/Memory (#5077)
* Whitelist System.Memory and System.ReadOnlyMemory (without pinning)
* Be very explicit with System.Span and System.ReadOnlySpan whitelists, do not allow pinnables
* Be completely explicit with Unsafe IL whitelist, allow Unsafe.SizeOf<T>
* BlacklistCodeWalker can have an allow list
* Compiler blacklist explicitly bans CreateSpan, allows Unsafe.SizeOf<T>
* Whitelist System.IO.InvalidDataException
* Better InlineArray whitelisting for compiler generated code
* WhitelistGen: Creates explicit signatures from wildcard rules
* Claude skill for whitelisting, uses the WhitelistGen tool to be very explicit and makes a good effort to security review
2026-06-17 18:28:47 +01:00

65 lines
2.4 KiB
C#

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
namespace Sandbox;
internal class BlacklistCodeWalker( SemanticModel semanticModel ) : CSharpSyntaxWalker
{
public static readonly DiagnosticDescriptor BlacklistDescriptor = new( "SB500", "Blacklist Error", "Prohibited type '{0}' used", "Sandbox.Compiling", DiagnosticSeverity.Error, true );
public List<Diagnostic> Diagnostics { get; set; } = [];
record LineSymbol( int Line, ISymbol Symbol );
HashSet<LineSymbol> LineSymbols = [];
private readonly SemanticModel _semanticModel = semanticModel;
static readonly SymbolDisplayFormat FullyQualifiedSymbolFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeContainingType,
parameterOptions: SymbolDisplayParameterOptions.IncludeType,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes
);
/// <summary>
/// Visits every node type, unless that node type has been explicitly overridden.
/// This gives us complete coverage of every type of possible syntax node.
/// From there we can see if it references any blacklisted symbols.
/// </summary>
public override void DefaultVisit( SyntaxNode node )
{
var symbolInfo = _semanticModel.GetSymbolInfo( node );
// Consider the CandidateSymbols even though you'd assume it can't compile.. It can!
var symbol = symbolInfo.Symbol ?? symbolInfo.CandidateSymbols.FirstOrDefault();
if ( symbol is null )
{
base.DefaultVisit( node );
return;
}
var fullyQualifiedName = symbol.ToDisplayString( FullyQualifiedSymbolFormat );
if ( !CompilerRules.IsBlocked( fullyQualifiedName ) )
{
base.DefaultVisit( node );
return;
}
// This isn't strictly needed, it just makes the diagnostics a bit neater and easier to test against
var lineSymbol = new LineSymbol( node.GetLocation().GetMappedLineSpan().StartLinePosition.Line, symbol );
if ( LineSymbols.Add( lineSymbol ) == false )
{
base.DefaultVisit( node );
return;
}
Diagnostics.Add( Diagnostic.Create( BlacklistDescriptor, node.GetLocation(), fullyQualifiedName ) );
base.DefaultVisit( node );
}
}