@hoanghainh/komagu 2.2.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/assess/lsa/CHANGELOG.md +15 -0
- package/assess/lsa/scripts/attack_chains.py +573 -0
- package/assess/lsa/scripts/baseline.py +14 -0
- package/assess/lsa/scripts/detect_stack.py +2 -1
- package/assess/lsa/scripts/findings_to_sarif.py +11 -0
- package/assess/lsa/scripts/forecast.py +544 -0
- package/assess/lsa/scripts/lsa_common.py +1 -5
- package/assess/lsa/scripts/merge_findings.py +14 -0
- package/assess/lsa/scripts/redaction.py +118 -0
- package/assess/lsa/scripts/render_report.py +86 -0
- package/assess/lsa/tests/test_attack_chains.py +292 -0
- package/assess/lsa/tests/test_forecast.py +160 -0
- package/assess/lsa/tests/test_modern_stacks.py +13 -0
- package/contracts/schemas/api-inventory-diff.schema.json +213 -0
- package/contracts/schemas/attack-chain.schema.json +181 -0
- package/contracts/schemas/bas-scenario.schema.json +217 -0
- package/contracts/schemas/evidence-manifest.schema.json +117 -0
- package/contracts/schemas/finding.schema.json +7 -0
- package/contracts/schemas/forecast.schema.json +172 -0
- package/governance/scs/__init__.py +21 -0
- package/governance/scs/mapping.py +133 -0
- package/governance/scs/pack.py +354 -0
- package/operate/ai/context.py +22 -2
- package/operate/cli/__init__.py +1 -1
- package/operate/cli/commands/evidence.py +138 -0
- package/operate/cli/commands/inventory.py +187 -0
- package/operate/cli/commands/simulate.py +175 -58
- package/operate/cli/main.py +7 -3
- package/operate/inventory/__init__.py +22 -0
- package/operate/inventory/api_inventory.py +566 -0
- package/operate/simulate/__init__.py +16 -0
- package/operate/simulate/engine.py +370 -0
- package/operate/simulate/loader.py +158 -0
- package/operate/simulate/scenarios/idc-frontier-ransomware.json +47 -0
- package/operate/simulate/scenarios/metabase-cve-2026-72898.json +84 -0
- package/operate/simulate/scenarios/times-car-breach.json +74 -0
- package/operate/simulate/scenarios.py +52 -0
- package/operate/tests/test_ai_redaction.py +181 -0
- package/operate/tests/test_api_inventory.py +239 -0
- package/operate/tests/test_declarative_scenarios.py +169 -0
- package/operate/tests/test_evidence_pack.py +128 -0
- package/operate/tests/test_headless_ai.py +16 -1
- package/package.json +1 -1
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Comprehensive unit tests for Declarative Incident Scenarios (#317).
|
|
3
|
+
|
|
4
|
+
Validates:
|
|
5
|
+
1. bas-scenario.schema.json schema validation on built-in and custom scenarios.
|
|
6
|
+
2. Discovery of scenarios from files, directories, and built-ins.
|
|
7
|
+
3. DeclarativeScenario execution of HTTP and storage steps.
|
|
8
|
+
4. Governance safety gates: draft scenarios blocked without --allow-draft.
|
|
9
|
+
5. Destructive step safety gates: destructive steps blocked without --allow-destructive.
|
|
10
|
+
6. Parameter substitutions and CLI --set overrides.
|
|
11
|
+
7. METI SCS audit evidence generation (#271).
|
|
12
|
+
8. CLI commands: --list, --validate, --scenario-file, --evidence.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
import tempfile
|
|
21
|
+
import unittest
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|
25
|
+
if str(PROJECT_ROOT) not in sys.path:
|
|
26
|
+
sys.path.insert(0, str(PROJECT_ROOT))
|
|
27
|
+
|
|
28
|
+
from operate.cli.main import main as cli_main
|
|
29
|
+
from operate.cross_user_tester.mock_test_server import start_mock_server
|
|
30
|
+
from operate.simulate.engine import (
|
|
31
|
+
DeclarativeScenario,
|
|
32
|
+
generate_execution_evidence,
|
|
33
|
+
)
|
|
34
|
+
from operate.simulate.loader import (
|
|
35
|
+
BUILTIN_SCENARIOS_DIR,
|
|
36
|
+
discover_scenarios,
|
|
37
|
+
load_scenario_file,
|
|
38
|
+
load_validator,
|
|
39
|
+
validate_scenario,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class TestDeclarativeScenarios(unittest.TestCase):
|
|
44
|
+
@classmethod
|
|
45
|
+
def setUpClass(cls) -> None:
|
|
46
|
+
cls.server, cls.thread, cls.base_url = start_mock_server(port=0)
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def tearDownClass(cls) -> None:
|
|
50
|
+
cls.server.server_close()
|
|
51
|
+
|
|
52
|
+
def test_schema_validates_builtin_scenarios(self) -> None:
|
|
53
|
+
"""All 3 built-in scenario files must strictly conform to bas-scenario.schema.json."""
|
|
54
|
+
validator = load_validator("bas-scenario.schema.json")
|
|
55
|
+
for sc_name in ["times-car-breach.json", "metabase-cve-2026-72898.json", "idc-frontier-ransomware.json"]:
|
|
56
|
+
path = BUILTIN_SCENARIOS_DIR / sc_name
|
|
57
|
+
self.assertTrue(path.is_file(), f"Missing built-in scenario: {sc_name}")
|
|
58
|
+
data = load_scenario_file(path)
|
|
59
|
+
errs = validator(data)
|
|
60
|
+
self.assertEqual(errs, [], f"Validation errors for {sc_name}: {errs}")
|
|
61
|
+
|
|
62
|
+
def test_schema_validation_error_on_invalid_scenario(self) -> None:
|
|
63
|
+
"""Validator must report missing required properties."""
|
|
64
|
+
invalid_data = {
|
|
65
|
+
"id": "invalid-scenario",
|
|
66
|
+
"title": "Invalid",
|
|
67
|
+
# missing status, version, incident, mappings, etc.
|
|
68
|
+
}
|
|
69
|
+
errs = validate_scenario(invalid_data)
|
|
70
|
+
self.assertTrue(len(errs) > 0)
|
|
71
|
+
self.assertTrue(any("required property missing" in e or "is a required property" in e for e in errs))
|
|
72
|
+
|
|
73
|
+
def test_discovery_scenarios(self) -> None:
|
|
74
|
+
"""discover_scenarios must locate all built-in scenarios and custom ones."""
|
|
75
|
+
scenarios = discover_scenarios(include_builtins=True)
|
|
76
|
+
self.assertIn("times-car-breach", scenarios)
|
|
77
|
+
self.assertIn("metabase-cve-2026-72898", scenarios)
|
|
78
|
+
self.assertIn("idc-frontier-ransomware", scenarios)
|
|
79
|
+
|
|
80
|
+
# Test custom dir discovery
|
|
81
|
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
82
|
+
custom_path = Path(tmp_dir) / "custom-sc.json"
|
|
83
|
+
base_sc = dict(scenarios["times-car-breach"])
|
|
84
|
+
base_sc["id"] = "custom-test-scenario"
|
|
85
|
+
custom_path.write_text(json.dumps(base_sc), encoding="utf-8")
|
|
86
|
+
|
|
87
|
+
discovered = discover_scenarios(scenario_dirs=[tmp_dir], include_builtins=False)
|
|
88
|
+
self.assertIn("custom-test-scenario", discovered)
|
|
89
|
+
|
|
90
|
+
def test_draft_scenario_blocked_without_override(self) -> None:
|
|
91
|
+
"""Scenario with status != 'approved' must fail execution unless allow_draft is set."""
|
|
92
|
+
data = dict(load_scenario_file(BUILTIN_SCENARIOS_DIR / "times-car-breach.json"))
|
|
93
|
+
data["status"] = "draft"
|
|
94
|
+
runner = DeclarativeScenario(data, self.base_url, allow_draft=False)
|
|
95
|
+
with self.assertRaises(PermissionError):
|
|
96
|
+
runner.run()
|
|
97
|
+
|
|
98
|
+
# Overriding with allow_draft=True must succeed
|
|
99
|
+
runner_override = DeclarativeScenario(data, self.base_url, allow_draft=True)
|
|
100
|
+
res = runner_override.run()
|
|
101
|
+
self.assertIsNotNone(res)
|
|
102
|
+
|
|
103
|
+
def test_destructive_step_blocked_without_override(self) -> None:
|
|
104
|
+
"""Destructive steps must be skipped unless allow_destructive is set."""
|
|
105
|
+
data = dict(load_scenario_file(BUILTIN_SCENARIOS_DIR / "times-car-breach.json"))
|
|
106
|
+
data["steps"] = [dict(data["steps"][0])]
|
|
107
|
+
data["steps"][0]["destructive"] = True
|
|
108
|
+
|
|
109
|
+
runner_safe = DeclarativeScenario(data, self.base_url, allow_destructive=False)
|
|
110
|
+
res_safe = runner_safe.run()
|
|
111
|
+
self.assertTrue(any(d.get("skipped") for d in res_safe.details))
|
|
112
|
+
|
|
113
|
+
runner_destr = DeclarativeScenario(data, self.base_url, allow_destructive=True)
|
|
114
|
+
res_destr = runner_destr.run()
|
|
115
|
+
self.assertFalse(any(d.get("skipped") for d in res_destr.details))
|
|
116
|
+
|
|
117
|
+
def test_parameter_override_and_substitution(self) -> None:
|
|
118
|
+
"""Custom parameter overrides must take precedence over defaults."""
|
|
119
|
+
data = load_scenario_file(BUILTIN_SCENARIOS_DIR / "times-car-breach.json")
|
|
120
|
+
runner = DeclarativeScenario(
|
|
121
|
+
data,
|
|
122
|
+
self.base_url,
|
|
123
|
+
param_overrides={
|
|
124
|
+
"endpoint_pattern": "/api/orders/{id}",
|
|
125
|
+
"sample_ids": ["999"],
|
|
126
|
+
},
|
|
127
|
+
)
|
|
128
|
+
res = runner.run()
|
|
129
|
+
self.assertEqual(res.total_attempts, 1)
|
|
130
|
+
self.assertTrue(res.passed)
|
|
131
|
+
|
|
132
|
+
def test_evidence_generation(self) -> None:
|
|
133
|
+
"""generate_execution_evidence produces complete audit record conforming to METI SCS."""
|
|
134
|
+
data = load_scenario_file(BUILTIN_SCENARIOS_DIR / "times-car-breach.json")
|
|
135
|
+
runner = DeclarativeScenario(data, self.base_url)
|
|
136
|
+
res = runner.run()
|
|
137
|
+
|
|
138
|
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
139
|
+
ev_path = Path(tmp_dir) / "evidence.json"
|
|
140
|
+
ev = generate_execution_evidence([runner], [res], self.base_url, output_path=ev_path)
|
|
141
|
+
self.assertTrue(ev_path.is_file())
|
|
142
|
+
self.assertEqual(ev["evidence_type"], "breach_and_attack_simulation_audit")
|
|
143
|
+
self.assertEqual(len(ev["audit_records"]), 1)
|
|
144
|
+
self.assertTrue(ev["audit_records"][0]["scenario_hash"].startswith("sha256:"))
|
|
145
|
+
|
|
146
|
+
def test_cli_list_and_validate(self) -> None:
|
|
147
|
+
"""Test CLI flags --list and --validate."""
|
|
148
|
+
# --list
|
|
149
|
+
exit_code = cli_main(["simulate", "--list"])
|
|
150
|
+
self.assertEqual(exit_code, 0)
|
|
151
|
+
|
|
152
|
+
# --validate valid
|
|
153
|
+
valid_file = str(BUILTIN_SCENARIOS_DIR / "times-car-breach.json")
|
|
154
|
+
exit_code = cli_main(["simulate", "--validate", valid_file])
|
|
155
|
+
self.assertEqual(exit_code, 0)
|
|
156
|
+
|
|
157
|
+
# --validate invalid
|
|
158
|
+
with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False) as f:
|
|
159
|
+
f.write("{\"id\": \"incomplete\"}")
|
|
160
|
+
inv_file = f.name
|
|
161
|
+
try:
|
|
162
|
+
exit_code = cli_main(["simulate", "--validate", inv_file])
|
|
163
|
+
self.assertEqual(exit_code, 2)
|
|
164
|
+
finally:
|
|
165
|
+
os.unlink(inv_file)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
if __name__ == "__main__":
|
|
169
|
+
unittest.main()
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Comprehensive unit tests for METI SCS Evidence Pack and Gap Report (#271).
|
|
3
|
+
|
|
4
|
+
Validates:
|
|
5
|
+
1. evidence-manifest.schema.json schema conformity.
|
|
6
|
+
2. Control mapping evaluation and readiness rating logic (★3, ★4, GAP).
|
|
7
|
+
3. ZIP archive bundling, SHA-256 cryptographic digests, and manifest structure.
|
|
8
|
+
4. Bilingual gap report generation (gap-report.ja.md and gap-report.md).
|
|
9
|
+
5. CLI execution `komagu evidence`, argument parsing, and quality gating (--require-tier).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import os
|
|
16
|
+
import sys
|
|
17
|
+
import tempfile
|
|
18
|
+
import unittest
|
|
19
|
+
import zipfile
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|
23
|
+
if str(PROJECT_ROOT) not in sys.path:
|
|
24
|
+
sys.path.insert(0, str(PROJECT_ROOT))
|
|
25
|
+
|
|
26
|
+
from assess.lsa.scripts.merge_findings import load_validator
|
|
27
|
+
from governance.scs.mapping import SCS_CONTROLS
|
|
28
|
+
from governance.scs.pack import EvidencePackBuilder
|
|
29
|
+
from operate.cli.main import main as cli_main
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class TestEvidencePack(unittest.TestCase):
|
|
33
|
+
def setUp(self) -> None:
|
|
34
|
+
self.validate_schema = load_validator("evidence-manifest.schema.json")
|
|
35
|
+
|
|
36
|
+
def test_schema_conformity_on_evaluated_manifest(self) -> None:
|
|
37
|
+
"""Generated evidence manifest must strictly conform to evidence-manifest.schema.json."""
|
|
38
|
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
39
|
+
# Create synthetic audit artifacts
|
|
40
|
+
audit_dir = Path(tmp_dir) / "audit"
|
|
41
|
+
audit_dir.mkdir()
|
|
42
|
+
(audit_dir / "findings.sarif").write_text("{}", encoding="utf-8")
|
|
43
|
+
(audit_dir / "sbom-cyclonedx.json").write_text("{}", encoding="utf-8")
|
|
44
|
+
(audit_dir / "api-inventory-diff.json").write_text("{}", encoding="utf-8")
|
|
45
|
+
(audit_dir / "coraza.conf").write_text("SecRuleEngine On", encoding="utf-8")
|
|
46
|
+
(audit_dir / "bas-evidence.json").write_text("{}", encoding="utf-8")
|
|
47
|
+
|
|
48
|
+
builder = EvidencePackBuilder(
|
|
49
|
+
audit_dirs=[audit_dir],
|
|
50
|
+
project_name="Test Enterprise Portal",
|
|
51
|
+
target_system="Production-Kubernetes",
|
|
52
|
+
)
|
|
53
|
+
arts = builder.discover_artifacts()
|
|
54
|
+
ctrls, readiness = builder.evaluate_controls(arts)
|
|
55
|
+
manifest = builder.generate_manifest(ctrls, readiness, arts)
|
|
56
|
+
|
|
57
|
+
errs = self.validate_schema(manifest)
|
|
58
|
+
self.assertEqual(errs, [], f"Schema validation errors: {errs}")
|
|
59
|
+
self.assertEqual(manifest["project_name"], "Test Enterprise Portal")
|
|
60
|
+
self.assertEqual(manifest["overall_readiness"]["rating"], "SCS-★4-READY")
|
|
61
|
+
|
|
62
|
+
def test_pack_builder_generates_zip_and_reports(self) -> None:
|
|
63
|
+
"""build_pack must package evidence-pack.zip, manifest JSON, and bilingual reports."""
|
|
64
|
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
65
|
+
audit_dir = Path(tmp_dir) / "audit"
|
|
66
|
+
audit_dir.mkdir()
|
|
67
|
+
(audit_dir / "findings.sarif").write_text('{"runs": []}', encoding="utf-8")
|
|
68
|
+
(audit_dir / "sbom-cyclonedx.json").write_text('{"bomFormat": "CycloneDX"}', encoding="utf-8")
|
|
69
|
+
|
|
70
|
+
out_dir = Path(tmp_dir) / "output"
|
|
71
|
+
|
|
72
|
+
builder = EvidencePackBuilder(
|
|
73
|
+
audit_dirs=[audit_dir],
|
|
74
|
+
project_name="Tokyo Logistics Monolith",
|
|
75
|
+
)
|
|
76
|
+
zip_p, manifest_p, ja_p, en_p = builder.build_pack(out_dir)
|
|
77
|
+
|
|
78
|
+
self.assertTrue(zip_p.is_file())
|
|
79
|
+
self.assertTrue(manifest_p.is_file())
|
|
80
|
+
self.assertTrue(ja_p.is_file())
|
|
81
|
+
self.assertTrue(en_p.is_file())
|
|
82
|
+
|
|
83
|
+
# Verify Japanese report content
|
|
84
|
+
ja_content = ja_p.read_text(encoding="utf-8")
|
|
85
|
+
self.assertIn("サプライチェーン・サイバーセキュリティ", ja_content)
|
|
86
|
+
self.assertIn("NIST CSF 2.0", ja_content)
|
|
87
|
+
self.assertIn("Tokyo Logistics Monolith", ja_content)
|
|
88
|
+
|
|
89
|
+
# Verify English report content
|
|
90
|
+
en_content = en_p.read_text(encoding="utf-8")
|
|
91
|
+
self.assertIn("METI Supply Chain Security", en_content)
|
|
92
|
+
self.assertIn("Tokyo Logistics Monolith", en_content)
|
|
93
|
+
|
|
94
|
+
# Verify ZIP contents
|
|
95
|
+
with zipfile.ZipFile(zip_p, "r") as zf:
|
|
96
|
+
names = zf.namelist()
|
|
97
|
+
self.assertIn("evidence-manifest.json", names)
|
|
98
|
+
self.assertIn("gap-report.ja.md", names)
|
|
99
|
+
self.assertIn("gap-report.md", names)
|
|
100
|
+
self.assertIn("artifacts/findings.sarif", names)
|
|
101
|
+
self.assertIn("artifacts/sbom-cyclonedx.json", names)
|
|
102
|
+
|
|
103
|
+
def test_cli_evidence_command_execution_and_gating(self) -> None:
|
|
104
|
+
"""Test `komagu evidence` CLI invocation and tier requirements."""
|
|
105
|
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
106
|
+
audit_dir = Path(tmp_dir) / "audit"
|
|
107
|
+
audit_dir.mkdir()
|
|
108
|
+
(audit_dir / "findings.sarif").write_text("{}", encoding="utf-8")
|
|
109
|
+
|
|
110
|
+
out_dir = Path(tmp_dir) / "pack"
|
|
111
|
+
|
|
112
|
+
# Execute without gate -> should succeed
|
|
113
|
+
code = cli_main(["evidence", "--audit-dir", str(audit_dir), "--output-dir", str(out_dir)])
|
|
114
|
+
self.assertEqual(code, 0)
|
|
115
|
+
self.assertTrue((out_dir / "evidence-pack.zip").is_file())
|
|
116
|
+
|
|
117
|
+
# Require tier 4 with only 1 artifact -> should fail quality gate
|
|
118
|
+
code_gate = cli_main([
|
|
119
|
+
"evidence",
|
|
120
|
+
"--audit-dir", str(audit_dir),
|
|
121
|
+
"--output-dir", str(out_dir),
|
|
122
|
+
"--require-tier", "4",
|
|
123
|
+
])
|
|
124
|
+
self.assertEqual(code_gate, 1)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
if __name__ == "__main__":
|
|
128
|
+
unittest.main()
|
|
@@ -19,7 +19,7 @@ if str(PROJECT_ROOT) not in sys.path:
|
|
|
19
19
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
20
20
|
|
|
21
21
|
from operate.ai.client import LLMClient
|
|
22
|
-
from operate.ai.mock_server import start_mock_llm_server
|
|
22
|
+
from operate.ai.mock_server import MockLLMHandler, start_mock_llm_server
|
|
23
23
|
from operate.ai.runner import HeadlessAIRunner
|
|
24
24
|
from operate.cli.main import main as cli_main
|
|
25
25
|
|
|
@@ -295,6 +295,21 @@ module.exports = app;
|
|
|
295
295
|
self.assertEqual(stats["parse_failures"], {})
|
|
296
296
|
self.assertEqual(stats["anchoring"]["unanchored"], [])
|
|
297
297
|
|
|
298
|
+
def test_no_planted_secret_reaches_the_model(self) -> None:
|
|
299
|
+
planted = ("PlantedPg9", "planted-key-42", "PlantedOra3")
|
|
300
|
+
with (self.target_dir / "app.js").open("a", encoding="utf-8") as fh:
|
|
301
|
+
fh.write('const DB = "postgres://app:PlantedPg9@db/orders";\nconst apiKey = "planted-key-42";\n'
|
|
302
|
+
'// legacy: db.password=PlantedOra3\n')
|
|
303
|
+
self._prescan()
|
|
304
|
+
client = LLMClient(endpoint=self.base_url, model="mock-model", api_key="test")
|
|
305
|
+
HeadlessAIRunner(self.target_dir, self.out_dir, client).run()
|
|
306
|
+
|
|
307
|
+
sent = json.dumps(MockLLMHandler.received_requests)
|
|
308
|
+
self.assertIn("5-finding-verifier", json.loads((self.out_dir / "ai-run-stats.json").read_text())["phases"])
|
|
309
|
+
self.assertIn("[REDACTED]", sent)
|
|
310
|
+
for secret in planted:
|
|
311
|
+
self.assertNotIn(secret, sent)
|
|
312
|
+
|
|
298
313
|
def test_unparseable_verifier_reply_leaves_findings_unverified(self) -> None:
|
|
299
314
|
"""A broken verifier reply must not confirm findings (#286)."""
|
|
300
315
|
self.server.server_close()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hoanghainh/komagu",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Security-First Legacy Modernization Platform CLI (LSA Kit, IAM Perimeter, Cross-User Testing)",
|
|
5
5
|
"main": "remediate/authz/nodejs/src/index.js",
|
|
6
6
|
"types": "remediate/authz/nodejs/src/index.d.ts",
|