fix(scriptanalysis): M3.6 — full-framework analysis refs close forbidden-type-in-allowed-ns blind spot; pin Process/Stopwatch; fix stale codec test; drop dead ContainsInCode
This commit is contained in:
@@ -2,12 +2,11 @@ namespace ZB.MOM.WW.ScadaBridge.TemplateEngine.Validation;
|
||||
|
||||
/// <summary>
|
||||
/// String/comment-aware scanner for the balanced-delimiter ("does it look like
|
||||
/// valid C#") checks used by <see cref="ScriptCompiler"/> and
|
||||
/// <c>SharedScriptService.ValidateSyntax</c>.
|
||||
/// valid C#") check used by <c>SharedScriptService.ValidateSyntax</c>.
|
||||
///
|
||||
/// <para>
|
||||
/// This is <b>not</b> a compiler. It is an interim structural check that walks
|
||||
/// the source once and tracks <c>{}</c>, <c>[]</c> and <c>()</c> depth while
|
||||
/// This is <b>not</b> a compiler. It is a structural check that walks the
|
||||
/// source once and tracks <c>{}</c>, <c>[]</c> and <c>()</c> depth while
|
||||
/// correctly skipping over the C# lexical constructs in which a delimiter is
|
||||
/// inert: line/block comments, regular string literals (with <c>\</c> escapes),
|
||||
/// verbatim strings (<c>@"..."</c>, where <c>""</c> escapes a quote and <c>\</c>
|
||||
@@ -17,11 +16,11 @@ namespace ZB.MOM.WW.ScadaBridge.TemplateEngine.Validation;
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// It is intentionally conservative: when the real Roslyn-based compiler is
|
||||
/// wired in (see <see cref="ScriptCompiler"/>) this hand-rolled scan should be
|
||||
/// replaced by <c>CSharpSyntaxTree.ParseText</c> diagnostics. Until then this
|
||||
/// scanner removes the false positives that a naive character count produced
|
||||
/// for valid scripts containing a delimiter inside a string or comment.
|
||||
/// Trust enforcement and full compilation are NOT done here — those are the
|
||||
/// authoritative <see cref="ScriptCompiler"/> (which delegates to the shared
|
||||
/// <c>ZB.MOM.WW.ScadaBridge.ScriptAnalysis</c> validator + Roslyn compile). This
|
||||
/// scanner only provides <c>SharedScriptService</c> a cheap pre-compile sanity
|
||||
/// check for balanced delimiters.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
internal static class CSharpDelimiterScanner
|
||||
@@ -41,121 +40,6 @@ internal static class CSharpDelimiterScanner
|
||||
UnterminatedChar,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true when <paramref name="pattern"/> occurs in a <b>code</b>
|
||||
/// region of <paramref name="code"/> — i.e. not wholly inside a string
|
||||
/// literal, char literal, or comment. Used by the interim forbidden-API
|
||||
/// scan so that the inert text <c>System.IO.</c> in a comment or string
|
||||
/// literal is not flagged as a forbidden API call (TemplateEngine-006).
|
||||
///
|
||||
/// <para>
|
||||
/// This removes the false-positive half of the substring scan. It does
|
||||
/// <b>not</b> close the bypass half: namespace aliases, <c>using static</c>,
|
||||
/// and <c>global::</c>-qualified references still evade a pure text match.
|
||||
/// Authoritative forbidden-API enforcement requires Roslyn semantic symbol
|
||||
/// analysis and is deferred to the real script compiler / Site Runtime
|
||||
/// sandbox; this check is advisory only.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="code">The C# source code to scan.</param>
|
||||
/// <param name="pattern">The substring to search for in code regions only.</param>
|
||||
/// <returns><c>true</c> if <paramref name="pattern"/> occurs in a code region (not inside a comment, string, or char literal); otherwise <c>false</c>.</returns>
|
||||
internal static bool ContainsInCode(string code, string pattern)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pattern))
|
||||
return false;
|
||||
|
||||
// Blank out every string/char-literal/comment span, then do an ordinary
|
||||
// substring search over what remains (the code regions).
|
||||
var codeOnly = BlankNonCodeSpans(code);
|
||||
return codeOnly.Contains(pattern, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the content of every comment, string literal, and char literal
|
||||
/// with spaces (newlines preserved), leaving only code regions intact.
|
||||
/// Delimiter characters themselves are also blanked so a pattern cannot
|
||||
/// straddle a literal boundary.
|
||||
/// </summary>
|
||||
private static string BlankNonCodeSpans(string code)
|
||||
{
|
||||
var buffer = code.ToCharArray();
|
||||
int n = code.Length;
|
||||
int i = 0;
|
||||
|
||||
void Blank(int from, int to)
|
||||
{
|
||||
for (int k = from; k < to && k < n; k++)
|
||||
if (buffer[k] != '\n' && buffer[k] != '\r')
|
||||
buffer[k] = ' ';
|
||||
}
|
||||
|
||||
while (i < n)
|
||||
{
|
||||
char c = code[i];
|
||||
char next = i + 1 < n ? code[i + 1] : '\0';
|
||||
int start = i;
|
||||
|
||||
if (c == '/' && next == '/')
|
||||
{
|
||||
i += 2;
|
||||
while (i < n && code[i] != '\n') i++;
|
||||
Blank(start, i);
|
||||
continue;
|
||||
}
|
||||
if (c == '/' && next == '*')
|
||||
{
|
||||
i += 2;
|
||||
while (i < n && !(code[i] == '*' && i + 1 < n && code[i + 1] == '/')) i++;
|
||||
if (i < n) i += 2;
|
||||
Blank(start, i);
|
||||
continue;
|
||||
}
|
||||
if (c == '"' && next == '"' && i + 2 < n && code[i + 2] == '"')
|
||||
{
|
||||
SkipRawString(code, ref i);
|
||||
Blank(start, i);
|
||||
continue;
|
||||
}
|
||||
if (c == '$')
|
||||
{
|
||||
int j = i + 1;
|
||||
bool verbatim = false;
|
||||
if (j < n && code[j] == '@') { verbatim = true; j++; }
|
||||
if (j < n && code[j] == '"')
|
||||
{
|
||||
i = j;
|
||||
SkipInterpolatedString(code, ref i, verbatim);
|
||||
Blank(start, i);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (c == '@' && next == '"')
|
||||
{
|
||||
i++;
|
||||
SkipVerbatimString(code, ref i);
|
||||
Blank(start, i);
|
||||
continue;
|
||||
}
|
||||
if (c == '"')
|
||||
{
|
||||
SkipRegularString(code, ref i);
|
||||
Blank(start, i);
|
||||
continue;
|
||||
}
|
||||
if (c == '\'')
|
||||
{
|
||||
SkipCharLiteral(code, ref i);
|
||||
Blank(start, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return new string(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Walks <paramref name="code"/> once and reports the first structural
|
||||
/// delimiter problem, or <see cref="Mismatch.None"/> when the source is
|
||||
|
||||
Reference in New Issue
Block a user