MSDO Toolchain Version Probe #10
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: MSDO Toolchain Version Probe | |
| # Runs MSDO to install tools as a side effect, then scrapes the install | |
| # directories to record exact resolved versions into toolchain-versions.json. | |
| # The breach monitor reads this file instead of guessing "latest" from registries. | |
| # | |
| # Guardian installs all tool wrappers as NuGet packages into: | |
| # /home/runner/work/_msdo/packages/nuget/{PackageName}.{version}/ | |
| # ESLint is installed via npm into: | |
| # /home/runner/work/_msdo/packages/node_modules/eslint/ | |
| # Package names confirmed from run 23433052319. | |
| on: | |
| schedule: | |
| - cron: '0 4 * * 1' # Weekly Monday 04:00 UTC | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| jobs: | |
| probe: | |
| name: Resolve and record MSDO tool versions | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| # Run MSDO — scan may find nothing (no real targets), that's fine. | |
| # Side effect: Guardian downloads all tool packages into _msdo/packages/nuget/. | |
| - name: Install MSDO tools | |
| uses: microsoft/security-devops-action@main | |
| continue-on-error: true | |
| with: | |
| tools: bandit,binskim,checkov,eslint,templateanalyzer,terrascan,trivy | |
| - name: Collect resolved tool versions from install dirs | |
| run: | | |
| python3 - <<'PYEOF' | |
| import os, json, re, pathlib, datetime | |
| NUGET_DIR = pathlib.Path('/home/runner/work/_msdo/packages/nuget') | |
| NPM_DIR = pathlib.Path('/home/runner/work/_msdo/packages/node_modules') | |
| VER_PAT = re.compile(r'^(.+?)\.(v?\d+\.\d+(?:\.\d+)*(?:[-+][0-9A-Za-z.-]+)?)$', re.IGNORECASE) | |
| # Guardian NuGet wrapper package names → canonical tool names. | |
| # Confirmed from run 23433052319 (_msdo/packages/nuget/ directory listing). | |
| PKG_TO_TOOL = { | |
| 'microsoft.guardian.banditredist_linux_amd64': 'bandit', | |
| 'microsoft.codeanalysis.binskim': 'binskim', | |
| 'microsoft.guardian.checkovredist_linux_amd64': 'checkov', | |
| 'azure.templates.analyzer.commandline.linux-x64': 'templateanalyzer', | |
| 'microsoft.guardian.terrascanredist_linux_amd64': 'terrascan', | |
| 'microsoft.guardian.trivyredist_linux_amd64': 'trivy', | |
| } | |
| # Internal packages — skip | |
| SKIP_PKGS = { | |
| 'microsoft.security.devops.cli', | |
| 'microsoft.security.devops.cli.linux-x64', | |
| 'microsoft.security.devops.cli.linux-arm64', | |
| 'microsoft.security.devops.cli.win-x64', | |
| 'microsoft.security.devops.policy.names', | |
| 'microsoft.security.devops.policy.github', | |
| } | |
| tools = {} | |
| raw_dirs = [] | |
| if NUGET_DIR.exists(): | |
| entries = sorted(d.name for d in NUGET_DIR.iterdir() if d.is_dir()) | |
| raw_dirs = entries | |
| for name in entries: | |
| m = VER_PAT.match(name) | |
| if not m: | |
| continue | |
| pkg_lower = m.group(1).lower() | |
| version = m.group(2) | |
| if pkg_lower in SKIP_PKGS: | |
| continue | |
| canonical = PKG_TO_TOOL.get(pkg_lower) | |
| if canonical: | |
| tools[canonical] = version | |
| # ESLint: installed via npm, read version from package.json | |
| eslint_pkg = NPM_DIR / 'eslint' / 'package.json' | |
| if eslint_pkg.exists(): | |
| tools['eslint'] = json.loads(eslint_pkg.read_text())['version'] | |
| print('raw_dirs:', raw_dirs) | |
| print('resolved:', tools) | |
| if not tools: | |
| raise SystemExit('ERROR: no versions resolved — _msdo/packages/nuget/ empty or missing. Aborting.') | |
| missing = (set(PKG_TO_TOOL.values()) | {'eslint'}) - set(tools.keys()) | |
| if missing: | |
| print(f'WARNING: expected tools not found: {sorted(missing)}') | |
| output = { | |
| 'generated_at': datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ'), | |
| 'msdo_cli_version': os.environ.get('MSDO_INSTALLEDVERSION', 'unknown'), | |
| 'tools': tools, | |
| 'raw_dirs': raw_dirs, | |
| } | |
| out = pathlib.Path('.github/toolchain-versions.json') | |
| out.parent.mkdir(parents=True, exist_ok=True) | |
| out.write_text(json.dumps(output, indent=2) + '\n') | |
| print(json.dumps(output, indent=2)) | |
| PYEOF | |
| - name: Commit updated versions | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add .github/toolchain-versions.json | |
| if git diff --cached --quiet; then | |
| echo "toolchain-versions.json unchanged — nothing to commit" | |
| else | |
| git commit -m "chore(ci): update toolchain-versions.json [skip ci]" | |
| # Push to dedicated unprotected branch — main has branch protection | |
| # requiring PRs. The breach monitor reads from this branch via API. | |
| git push origin HEAD:bot/toolchain-versions --force | |
| fi |