Skip to content

Commit 4721631

Browse files
authored
Add searchable NuGet security policy metadata (#4459) (#4520)
* Add NuGet security policy extraction (#4459) * Reindex XML after extractor upgrade (#4459)
1 parent 6712e6a commit 4721631

8 files changed

Lines changed: 230 additions & 4 deletions

File tree

DEVELOPER_GUIDE.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,7 @@ Extractor strategy by language surface:
10091009
| Common Lisp / Racket | Use a lightweight S-expression scanner that masks strings, line comments, and `#| ... |#` block comments before extracting definitions and function ranges. |
10101010
| HTML | Uses a dedicated character-level state machine instead of the regex pattern loop. It walks tag openers, quoted/unquoted attribute values including multi-line values, and masks `<script>` / `<style>` / `<textarea>` / `<title>` bodies plus `<!-- ... -->` comments so attribute-lookalike strings inside those regions do not leak phantom symbols. |
10111011
| JSON / JSON Lines | JSON emits `object`, `array`, `property`, and bounded primitive-array `value` symbols with indexed paths. `.jsonl` and `.ndjson` parse each non-empty physical line independently and prefix symbols with a stable zero-based record path such as `[0].result.path`. |
1012+
| XML / NuGet.config | Generic XML emits bounded element and attribute paths. NuGet.config additionally promotes package sources, source mappings, signature validation mode, trusted signer names, certificate fingerprints, and `allowUntrustedRoot` values to semantic `property` symbols with `nuget.*` subkinds. |
10121013

10131014
JavaScript and TypeScript export/reference details:
10141015

@@ -1061,6 +1062,7 @@ Supported symbol kinds by language:
10611062
| Lua / R / Haskell / F# | language-specific functions/types/modules/signatures | imports/requires/opens where supported | mixed |
10621063
| VB.NET | subs/functions, classes/modules, structures, interfaces, enums, properties, events | namespaces/imports, `AddressOf`, `Handles` | yes |
10631064
| Zig / PowerShell / CSS-SCSS / Batch / Assembly / HTML | language-specific functions, labels, selectors, stages, Web Components, properties, imports | language-specific references where implemented | mixed |
1065+
| XML | bounded element/attribute paths plus NuGet.config security-policy values | XAML references where implemented | mixed |
10641066

10651067
Shell and PowerShell files also expose a synthetic `<script>` function symbol spanning the file. Top-level call references use this scope as their graph container, while references inside declared functions retain the declared function container.
10661068

@@ -1072,7 +1074,7 @@ For JavaScript / TypeScript, reference extraction also captures tagged template
10721074

10731075
SQL also emits `namespace` symbols for `CREATE SCHEMA`, but the summary table above does not have a dedicated namespace column. SQL graph extraction emits `reference` edges for named source/target forms such as `FROM`, `JOIN`, `INSERT INTO`, `UPDATE`, `TRUNCATE TABLE`, `DELETE FROM`, `DELETE ... USING`, and `MERGE ... USING`; procedure and table-valued-function calls stay on the `call` path.
10741076

1075-
Additionally, 19 languages are detected and indexed as raw text without symbol extraction: cmake, clojure, crystal, dockerignore, d, editorconfig, erlang, gitignore, justfile, julia, markdown, nim, ocaml, svelte, tcl, toml, vue, xml, yaml.
1077+
Additionally, 18 languages are detected and indexed as raw text without symbol extraction: cmake, clojure, crystal, dockerignore, d, editorconfig, erlang, gitignore, justfile, julia, markdown, nim, ocaml, svelte, tcl, toml, vue, yaml.
10761078

10771079
VB.NET container patterns use `RegexOptions.IgnoreCase` plus `VisualBasicEnd`-based range tracking, so `Partial` spelling differences and multi-file type families still receive stable definition ranges and hotspot-family metadata.
10781080

@@ -3458,6 +3460,7 @@ LIMIT 20;
34583460
| Common Lisp / Racket | 文字列、行 comment、`#| ... |#` block comment を mask してから definition と function range を抽出する軽量な S-expression scanner を使います。 |
34593461
| HTML | 汎用の正規表現 loop を使わず、専用の文字単位 state machine で tag opener、引用符付き/なし attribute value(複数行値を含む)、`<script>` / `<style>` / `<textarea>` / `<title>` body、`<!-- ... -->` comment を扱い、attribute 名に似た body 内文字列から phantom symbol が漏れないようにします。 |
34603462
| JSON / JSON Lines | JSON は `object`、`array`、`property` と、上限付きの primitive-array `value` symbol を index 付き path で出力します。`.jsonl` と `.ndjson` は空でない物理行を個別に parse し、`[0].result.path` のような安定した 0 始まり record path を付けます。 |
3463+
| XML / NuGet.config | 汎用 XML は上限付きの element / attribute path を出力します。NuGet.config ではさらに package source、source mapping、署名検証モード、trusted signer 名、証明書 fingerprint、`allowUntrustedRoot` の値を `nuget.*` subkind 付きの semantic `property` symbol にします。 |
34613464

34623465
JavaScript / TypeScript の export / reference 詳細:
34633466

@@ -3510,6 +3513,7 @@ SQL 固有の symbol extraction:
35103513
| Lua / R / Haskell / F# | 言語別の function / type / module / signature | import / require / open など対応済み surface | mixed |
35113514
| VB.NET | Sub/Function、Class/Module、Structure、Interface、Enum、Property、Event | Namespace、Imports、`AddressOf`、`Handles` | yes |
35123515
| Zig / PowerShell / CSS-SCSS / Batch / Assembly / HTML | 言語別 function、label、selector、stage、Web Component、property、import | 実装済みの言語別 reference | mixed |
3516+
| XML | 上限付き element / attribute path と NuGet.config security-policy value | 実装済みの XAML reference | mixed |
35133517

35143518
Shell と PowerShell のファイルには、ファイル全体を覆う合成 `<script>` 関数シンボルも作成される。トップレベルの call reference はこのスコープを graph container として使い、宣言済み関数内の reference はその関数 container を維持する。
35153519

@@ -3521,7 +3525,7 @@ JavaScript / TypeScript では、reference extraction が `` gql`...` ``、`` st
35213525

35223526
SQL は `CREATE SCHEMA` から `namespace` シンボルも出力するが、上の要約表には namespace 専用列はない。SQL graph extraction は `FROM`、`JOIN`、`INSERT INTO`、`UPDATE`、`TRUNCATE TABLE`、`DELETE FROM`、`DELETE ... USING`、`MERGE ... USING` のような source/target 形を `reference` edge として出力し、procedure call と table-valued function 使用は `call` 経路に残す。
35233527

3524-
他に19言語がテキスト検索用に検出されるがシンボル抽出パターンは未対応: cmake, clojure, crystal, dockerignore, d, editorconfig, erlang, gitignore, justfile, julia, markdown, nim, ocaml, svelte, tcl, toml, vue, xml, yaml。
3528+
他に18言語がテキスト検索用に検出されるがシンボル抽出パターンは未対応: cmake, clojure, crystal, dockerignore, d, editorconfig, erlang, gitignore, justfile, julia, markdown, nim, ocaml, svelte, tcl, toml, vue, yaml。
35253529

35263530
正規表現ベースの抽出は意図的にシンプルです。AST精度よりも速度とポータビリティを優先しています。
35273531

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ Symbol and reference extraction are lightweight indexing hints optimized for
4444
speed, locality, explainability, and retrieval usefulness. Embeddings, vector
4545
search, and LLM-based semantic ranking are not assumptions of CodeIndex core.
4646

47+
NuGet.config XML receives security-policy symbols for package sources and source
48+
mappings, signature validation mode, trusted signers, certificate fingerprints,
49+
and `allowUntrustedRoot`, so these controls can be queried by their configured values.
50+
4751
## Contribution Policy
4852

4953
Issue reports, feature requests, and improvement suggestions are welcome.
@@ -259,6 +263,10 @@ Symbol / reference extraction は、速度、ローカル完結、説明可能
259263
寄せた lightweight indexing hint です。Embedding、vector search、LLM-based semantic
260264
ranking は CodeIndex core の前提機能ではありません。
261265

266+
NuGet.config XML では package source / source mapping、署名検証モード、trusted
267+
signer、証明書 fingerprint、`allowUntrustedRoot` をセキュリティポリシーの
268+
シンボルとして抽出するため、設定値からこれらの制御を検索できます。
269+
262270
## コントリビューション方針
263271

264272
不具合報告、機能要望、改善提案の Issue は歓迎します。
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
category: added
3+
issues:
4+
- 4459
5+
affected:
6+
- src/CodeIndex/Indexer/Symbols/SymbolExtractor.cs
7+
- src/CodeIndex/Indexer/Symbols/SymbolExtractor.StructuralMetadata.cs
8+
- tests/CodeIndex.Tests/IndexCommandRunnerTests.cs
9+
- tests/CodeIndex.Tests/SymbolExtractorXmlTests.cs
10+
- README.md
11+
- DEVELOPER_GUIDE.md
12+
---
13+
14+
## English
15+
16+
- **NuGet.config security policy is now directly searchable (#4459)** — XML extraction promotes package sources, source mappings, signature validation mode, trusted signer names, certificate fingerprints, and `allowUntrustedRoot` values to bounded semantic symbols.
17+
18+
## 日本語
19+
20+
- **NuGet.config のセキュリティポリシーを直接検索できるようになりました (#4459)** — XML 抽出は package source、source mapping、署名検証モード、trusted signer 名、証明書 fingerprint、`allowUntrustedRoot` の値を上限付き semantic symbol として出力します。

src/CodeIndex/Indexer/Symbols/SymbolExtractor.StructuralMetadata.cs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ private static List<SymbolRecord> ExtractGenericXmlSymbols(long fileId, string c
191191
reader.MoveToElement();
192192
}
193193

194+
if (!TryAddNuGetConfigValueSymbols(fileId, reader, path, lineNumber, lines, symbols, ref truncated))
195+
return symbols;
196+
194197
if (!reader.IsEmptyElement)
195198
{
196199
elementPaths.Push(path);
@@ -215,6 +218,88 @@ private static List<SymbolRecord> ExtractGenericXmlSymbols(long fileId, string c
215218
return TrimStructuredDataSymbols(symbols, fileId, "structured_data_xml_symbol_budget_exceeded", lines);
216219
}
217220

221+
private static bool TryAddNuGetConfigValueSymbols(
222+
long fileId,
223+
XmlReader reader,
224+
string path,
225+
int lineNumber,
226+
string[] lines,
227+
List<SymbolRecord> symbols,
228+
ref bool truncated)
229+
{
230+
if (!path.StartsWith("configuration.", StringComparison.OrdinalIgnoreCase))
231+
return true;
232+
233+
string? subKind = null;
234+
string? value = null;
235+
var localName = reader.LocalName;
236+
237+
if (path.Equals("configuration.packageSources.add", StringComparison.OrdinalIgnoreCase))
238+
{
239+
if (!TryAddNuGetConfigValueSymbol(fileId, "nuget.package_source", reader.GetAttribute("key"), lineNumber, lines, path, symbols, ref truncated))
240+
return false;
241+
subKind = "nuget.package_source_url";
242+
value = reader.GetAttribute("value");
243+
}
244+
else if (path.Equals("configuration.packageSourceMapping.packageSource", StringComparison.OrdinalIgnoreCase))
245+
{
246+
subKind = "nuget.package_source_mapping";
247+
value = reader.GetAttribute("key");
248+
}
249+
else if (path.EndsWith(".packageSource.package", StringComparison.OrdinalIgnoreCase)
250+
&& path.Contains(".packageSourceMapping.", StringComparison.OrdinalIgnoreCase))
251+
{
252+
subKind = "nuget.package_source_mapping_pattern";
253+
value = reader.GetAttribute("pattern");
254+
}
255+
else if (path.Equals("configuration.config.add", StringComparison.OrdinalIgnoreCase)
256+
&& string.Equals(reader.GetAttribute("key"), "signatureValidationMode", StringComparison.OrdinalIgnoreCase))
257+
{
258+
subKind = "nuget.signature_validation_mode";
259+
value = reader.GetAttribute("value");
260+
}
261+
else if (path.Contains(".trustedSigners.", StringComparison.OrdinalIgnoreCase)
262+
&& (localName.Equals("author", StringComparison.OrdinalIgnoreCase)
263+
|| localName.Equals("repository", StringComparison.OrdinalIgnoreCase)))
264+
{
265+
subKind = "nuget.trusted_signer";
266+
value = reader.GetAttribute("name");
267+
}
268+
else if (path.Contains(".trustedSigners.", StringComparison.OrdinalIgnoreCase)
269+
&& localName.Equals("certificate", StringComparison.OrdinalIgnoreCase))
270+
{
271+
if (!TryAddNuGetConfigValueSymbol(fileId, "nuget.certificate_fingerprint", reader.GetAttribute("fingerprint"), lineNumber, lines, path, symbols, ref truncated))
272+
return false;
273+
subKind = "nuget.allow_untrusted_root";
274+
value = reader.GetAttribute("allowUntrustedRoot");
275+
}
276+
277+
return TryAddNuGetConfigValueSymbol(fileId, subKind, value, lineNumber, lines, path, symbols, ref truncated);
278+
}
279+
280+
private static bool TryAddNuGetConfigValueSymbol(
281+
long fileId,
282+
string? semanticKind,
283+
string? semanticValue,
284+
int lineNumber,
285+
string[] lines,
286+
string path,
287+
List<SymbolRecord> symbols,
288+
ref bool truncated)
289+
{
290+
if (semanticKind == null || string.IsNullOrWhiteSpace(semanticValue))
291+
return true;
292+
293+
var normalizedValue = semanticValue.Trim();
294+
if (normalizedValue.Length > StructuredDataMaxPathLength)
295+
return true;
296+
if (!TryAddStructuredDataSymbol(fileId, "property", normalizedValue, lineNumber, lines, path, symbols, "structured_data_xml_symbol_budget_exceeded", ref truncated))
297+
return false;
298+
299+
symbols[^1].SubKind = semanticKind;
300+
return true;
301+
}
302+
218303
private static void AddManifestAssemblyIdentitySymbols(
219304
long fileId,
220305
string[] lines,

src/CodeIndex/Indexer/Symbols/SymbolExtractor.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public static partial class SymbolExtractor
1919
public const int DockerfileContractVersion = 2;
2020
public const int MakefileContractVersion = 2;
2121
public const int StyleAndXamlContractVersion = 2;
22+
public const int XmlContractVersion = 3;
2223
public const int FunctionalLanguageContractVersion = 2;
2324
public const int DynamicLanguageContractVersion = 2;
2425
public const int SystemsLanguageContractVersion = 2;
@@ -73,7 +74,8 @@ public static int GetContractVersion(string? lang)
7374
"csharp" => CSharpContractVersion,
7475
"dockerfile" => DockerfileContractVersion,
7576
"makefile" => MakefileContractVersion,
76-
"sass" or "stylus" or "xml" => StyleAndXamlContractVersion,
77+
"sass" or "stylus" => StyleAndXamlContractVersion,
78+
"xml" => XmlContractVersion,
7779
"clojure" or "erlang" or "ocaml" or "raku" => FunctionalLanguageContractVersion,
7880
"crystal" or "groovy" or "julia" or "tcl" => DynamicLanguageContractVersion,
7981
"ada" or "d" or "nim" => SystemsLanguageContractVersion,

tests/CodeIndex.Tests/IndexCommandRunnerTests.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5739,6 +5739,56 @@ def target():
57395739
}
57405740
}
57415741

5742+
[Fact]
5743+
public void Run_FilesUpdate_ReindexesUnchangedNuGetConfigWhenXmlExtractorVersionChanged_Issue4459()
5744+
{
5745+
var projectRoot = CreateTempProject();
5746+
try
5747+
{
5748+
var sourcePath = Path.Combine(projectRoot, "nuget.config");
5749+
File.WriteAllText(
5750+
sourcePath,
5751+
"""
5752+
<configuration>
5753+
<config>
5754+
<add key="signatureValidationMode" value="require" />
5755+
</config>
5756+
</configuration>
5757+
""");
5758+
5759+
var initialExitCode = IndexCommandRunner.Run([projectRoot, "--json"], _jsonOptions);
5760+
Assert.Equal(CommandExitCodes.Success, initialExitCode);
5761+
5762+
var dbPath = Path.Combine(projectRoot, ".cdidx", "codeindex.db");
5763+
using (var conn = OpenNonPoolingConnection(dbPath))
5764+
{
5765+
conn.Open();
5766+
using var cmd = conn.CreateCommand();
5767+
cmd.CommandText = """
5768+
DELETE FROM symbols WHERE sub_kind = 'nuget.signature_validation_mode';
5769+
UPDATE codeindex_meta SET value = '2' WHERE key = 'symbol_extractor_version_xml';
5770+
""";
5771+
cmd.ExecuteNonQuery();
5772+
}
5773+
5774+
var (exitCode, json) = RunAndCaptureJson([projectRoot, "--files", sourcePath, "--json"]);
5775+
5776+
Assert.Equal(CommandExitCodes.Success, exitCode);
5777+
Assert.Equal(1, json.GetProperty("summary").GetProperty("updated").GetInt32());
5778+
Assert.Equal(0, json.GetProperty("summary").GetProperty("skipped").GetInt32());
5779+
5780+
using var verify = OpenNonPoolingConnection(dbPath);
5781+
verify.Open();
5782+
using var symbolCmd = verify.CreateCommand();
5783+
symbolCmd.CommandText = "SELECT COUNT(*) FROM symbols WHERE name = 'require' AND sub_kind = 'nuget.signature_validation_mode'";
5784+
Assert.Equal(1L, (long)symbolCmd.ExecuteScalar()!);
5785+
}
5786+
finally
5787+
{
5788+
DeleteDirectory(projectRoot);
5789+
}
5790+
}
5791+
57425792
[Fact]
57435793
public void Run_FilesUpdate_ReindexesUnchangedJsonFileWhenExpandedLanguageExtractorVersionChanged()
57445794
{

tests/CodeIndex.Tests/SymbolExtractorTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12084,8 +12084,9 @@ public void GetContractVersion_LanguageSpecificKinds_UseDedicatedVersions()
1208412084
Assert.True(SymbolExtractor.MakefileContractVersion > SymbolExtractor.DefaultContractVersion);
1208512085
Assert.Equal(SymbolExtractor.StyleAndXamlContractVersion, SymbolExtractor.GetContractVersion("sass"));
1208612086
Assert.Equal(SymbolExtractor.StyleAndXamlContractVersion, SymbolExtractor.GetContractVersion("stylus"));
12087-
Assert.Equal(SymbolExtractor.StyleAndXamlContractVersion, SymbolExtractor.GetContractVersion("xml"));
1208812087
Assert.True(SymbolExtractor.StyleAndXamlContractVersion > SymbolExtractor.DefaultContractVersion);
12088+
Assert.Equal(SymbolExtractor.XmlContractVersion, SymbolExtractor.GetContractVersion("xml"));
12089+
Assert.True(SymbolExtractor.XmlContractVersion > SymbolExtractor.StyleAndXamlContractVersion);
1208912090
}
1209012091

1209112092
[Fact]

0 commit comments

Comments
 (0)