Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
235 lines
6.1 KiB
C#
235 lines
6.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
|
|
namespace StellaOps.Scanner.Analyzers.Lang.Go.Internal;
|
|
|
|
internal static class GoBuildInfoParser
|
|
{
|
|
private const string PathPrefix = "path\t";
|
|
private const string ModulePrefix = "mod\t";
|
|
private const string DependencyPrefix = "dep\t";
|
|
private const string ReplacementPrefix = "=>\t";
|
|
private const string BuildPrefix = "build\t";
|
|
|
|
public static bool TryParse(string goVersion, string absoluteBinaryPath, string rawModuleData, out GoBuildInfo? info)
|
|
{
|
|
info = null;
|
|
|
|
if (string.IsNullOrWhiteSpace(goVersion) || string.IsNullOrWhiteSpace(rawModuleData))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
string? modulePath = null;
|
|
GoModule? mainModule = null;
|
|
var dependencies = new List<GoModule>();
|
|
var settings = new SortedDictionary<string, string?>(StringComparer.Ordinal);
|
|
|
|
GoModule? lastModule = null;
|
|
using var reader = new StringReader(rawModuleData);
|
|
|
|
while (reader.ReadLine() is { } line)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(line))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (line.StartsWith(PathPrefix, StringComparison.Ordinal))
|
|
{
|
|
modulePath = line[PathPrefix.Length..].Trim();
|
|
continue;
|
|
}
|
|
|
|
if (line.StartsWith(ModulePrefix, StringComparison.Ordinal))
|
|
{
|
|
mainModule = ParseModule(line.AsSpan(ModulePrefix.Length), isMain: true);
|
|
lastModule = mainModule;
|
|
continue;
|
|
}
|
|
|
|
if (line.StartsWith(DependencyPrefix, StringComparison.Ordinal))
|
|
{
|
|
var dependency = ParseModule(line.AsSpan(DependencyPrefix.Length), isMain: false);
|
|
if (dependency is not null)
|
|
{
|
|
dependencies.Add(dependency);
|
|
lastModule = dependency;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (line.StartsWith(ReplacementPrefix, StringComparison.Ordinal))
|
|
{
|
|
if (lastModule is null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var replacement = ParseReplacement(line.AsSpan(ReplacementPrefix.Length));
|
|
if (replacement is not null)
|
|
{
|
|
lastModule.SetReplacement(replacement);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (line.StartsWith(BuildPrefix, StringComparison.Ordinal))
|
|
{
|
|
var pair = ParseBuildSetting(line.AsSpan(BuildPrefix.Length));
|
|
if (!string.IsNullOrEmpty(pair.Key))
|
|
{
|
|
settings[pair.Key] = pair.Value;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mainModule is null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(modulePath))
|
|
{
|
|
modulePath = mainModule.Path;
|
|
}
|
|
|
|
info = new GoBuildInfo(
|
|
goVersion,
|
|
absoluteBinaryPath,
|
|
modulePath,
|
|
mainModule,
|
|
dependencies,
|
|
settings);
|
|
|
|
return true;
|
|
}
|
|
|
|
private static GoModule? ParseModule(ReadOnlySpan<char> span, bool isMain)
|
|
{
|
|
var fields = SplitFields(span, expected: 4);
|
|
if (fields.Count == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var path = fields[0];
|
|
if (string.IsNullOrWhiteSpace(path))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var version = fields.Count > 1 ? fields[1] : null;
|
|
var sum = fields.Count > 2 ? fields[2] : null;
|
|
|
|
return new GoModule(path, version, sum, isMain);
|
|
}
|
|
|
|
private static GoModuleReplacement? ParseReplacement(ReadOnlySpan<char> span)
|
|
{
|
|
var fields = SplitFields(span, expected: 3);
|
|
if (fields.Count == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var path = fields[0];
|
|
if (string.IsNullOrWhiteSpace(path))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var version = fields.Count > 1 ? fields[1] : null;
|
|
var sum = fields.Count > 2 ? fields[2] : null;
|
|
|
|
return new GoModuleReplacement(path, version, sum);
|
|
}
|
|
|
|
private static KeyValuePair<string, string?> ParseBuildSetting(ReadOnlySpan<char> span)
|
|
{
|
|
span = span.Trim();
|
|
if (span.IsEmpty)
|
|
{
|
|
return default;
|
|
}
|
|
|
|
var separatorIndex = span.IndexOf('=');
|
|
if (separatorIndex <= 0)
|
|
{
|
|
return default;
|
|
}
|
|
|
|
var rawKey = span[..separatorIndex].Trim();
|
|
var rawValue = span[(separatorIndex + 1)..].Trim();
|
|
|
|
var key = Unquote(rawKey.ToString());
|
|
if (string.IsNullOrWhiteSpace(key))
|
|
{
|
|
return default;
|
|
}
|
|
|
|
var value = Unquote(rawValue.ToString());
|
|
return new KeyValuePair<string, string?>(key, value);
|
|
}
|
|
|
|
private static List<string> SplitFields(ReadOnlySpan<char> span, int expected)
|
|
{
|
|
var fields = new List<string>(expected);
|
|
var builder = new StringBuilder();
|
|
|
|
for (var i = 0; i < span.Length; i++)
|
|
{
|
|
var current = span[i];
|
|
if (current == '\t')
|
|
{
|
|
fields.Add(builder.ToString());
|
|
builder.Clear();
|
|
continue;
|
|
}
|
|
|
|
builder.Append(current);
|
|
}
|
|
|
|
fields.Add(builder.ToString());
|
|
return fields;
|
|
}
|
|
|
|
private static string Unquote(string value)
|
|
{
|
|
if (string.IsNullOrEmpty(value))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
value = value.Trim();
|
|
if (value.Length < 2)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (value[0] == '"' && value[^1] == '"')
|
|
{
|
|
try
|
|
{
|
|
return JsonSerializer.Deserialize<string>(value) ?? value;
|
|
}
|
|
catch (JsonException)
|
|
{
|
|
return value;
|
|
}
|
|
}
|
|
|
|
if (value[0] == '`' && value[^1] == '`')
|
|
{
|
|
return value[1..^1];
|
|
}
|
|
|
|
return value;
|
|
}
|
|
}
|