Skip to content

Commit 2c50c77

Browse files
authored
Merge pull request #1493 from DeusData/harden/av-surface-strings
harden: ship integration templates as a verified JSON asset, not embedded bytes
2 parents 6a1ef1e + 497c680 commit 2c50c77

21 files changed

Lines changed: 1382 additions & 431 deletions

‎Makefile.cbm‎

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ CFLAGS_COMMON = -std=c11 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall -Wextra -Werror \
5252
$(GCC_ONLY_FLAGS) \
5353
-Isrc -Ivendored -Ivendored/sqlite3 \
5454
-Ivendored/mimalloc/include \
55-
-I$(CBM_DIR) -I$(TS_INCLUDE)
55+
-I$(CBM_DIR) -I$(TS_INCLUDE) -I$(BUILD_DIR)/generated
5656

5757
CXXFLAGS_COMMON = -std=c++14 -Wall -Wextra -Werror \
5858
-Wno-unused-parameter \
@@ -355,7 +355,7 @@ GIT_SRCS = src/git/git_context.c
355355

356356
# CLI module (new)
357357
CLI_SRCS = src/cli/cli.c src/cli/progress_sink.c src/cli/hook_augment.c src/cli/client_adapter.c \
358-
src/cli/agent_clients.c src/cli/agent_profiles.c \
358+
src/cli/agent_clients.c src/cli/agent_profiles.c src/cli/integration_assets.c \
359359
src/cli/config_json_like.c src/cli/config_toml_edit.c src/cli/config_yaml_edit.c \
360360
src/cli/config_text_edit.c src/cli/activation_transaction.c
361361

@@ -470,6 +470,17 @@ EXISTING_C_SRCS = $(EXTRACTION_SRCS) $(LSP_SRCS) $(TS_RUNTIME_SRC) \
470470
PROJECT_HDRS = $(wildcard src/*.h src/*/*.h $(CBM_DIR)/*.h $(CBM_DIR)/lsp/*.h tests/*.h \
471471
tests/repro/*.h)
472472

473+
# ── Integration-asset integrity constant (generated, never hand-edited) ──
474+
# The binary embeds exactly ONE fact about the shipped integration templates:
475+
# the SHA-256 of assets/cbm-integrations.json, generated at build time (rule
476+
# below the BUILD_DIR definition; target lines expand immediately). Appended
477+
# to PROJECT_HDRS so every single-invocation target (test-runner,
478+
# test-repro-runner, test-runner-tsan, codebase-memory-mcp) rebuilds when the
479+
# asset changes — a stale constant would make every install fail verification.
480+
INTEGRATIONS_ASSET = assets/cbm-integrations.json
481+
INTEGRATIONS_HASH_HDR = $(BUILD_DIR)/generated/cbm_integrations_hash.h
482+
PROJECT_HDRS += $(INTEGRATIONS_HASH_HDR)
483+
473484
# ── Test sources ─────────────────────────────────────────────────
474485

475486
TEST_FOUNDATION_SRCS = \
@@ -685,6 +696,11 @@ ALL_TEST_SRCS =$(TEST_FOUNDATION_SRCS) $(TEST_EXTRACTION_SRCS) $(TEST_STORE_SRCS
685696

686697
BUILD_DIR = build/c
687698

699+
# Integration-asset hash header (variables defined next to PROJECT_HDRS; the
700+
# rule lives here because target lines expand $(BUILD_DIR) immediately).
701+
$(INTEGRATIONS_HASH_HDR): $(INTEGRATIONS_ASSET) scripts/gen-integrations-hash.sh | $(BUILD_DIR)
702+
sh scripts/gen-integrations-hash.sh $(INTEGRATIONS_ASSET) $@
703+
688704
# ── Object file compilation (grammars need relaxed warnings) ─────
689705

690706
# Grammar + tree-sitter runtime: compiled without -Werror (upstream code has warnings)
@@ -1041,7 +1057,11 @@ frontend:
10411057
embed: frontend
10421058
scripts/embed-frontend.sh graph-ui/dist $(BUILD_DIR)/embedded
10431059

1044-
cbm-with-ui: embed $(OBJS_VENDORED_PROD)
1060+
# $(INTEGRATIONS_HASH_HDR): generated header included by integration_assets.c.
1061+
# The standard target gets it via $(PROJECT_HDRS); the UI target compiles in one
1062+
# shot and must list it too, or the build fails with "cbm_integrations_hash.h
1063+
# file not found" (seen on the Windows UI build).
1064+
cbm-with-ui: embed $(INTEGRATIONS_HASH_HDR) $(OBJS_VENDORED_PROD)
10451065
$(CC) $(CFLAGS_PROD) -o $(BUILD_DIR)/codebase-memory-mcp \
10461066
$(MAIN_SRC) $(PROD_SRCS_WITH_ASSETS) \
10471067
$(EXTRACTION_SRCS) $(AC_LZ4_SRCS) $(ZSTD_SRCS) $(SQLITE_WRITER_SRC) \
@@ -1097,7 +1117,7 @@ lint-cppcheck:
10971117
--suppress=ctunullpointerOutOfMemory \
10981118
--suppress='nullPointer:internal/cbm/sqlite_writer.c' \
10991119
-Isrc -Ivendored -Ivendored/sqlite3 \
1100-
-I$(CBM_DIR) -I$(TS_INCLUDE) \
1120+
-I$(CBM_DIR) -I$(TS_INCLUDE) -I$(BUILD_DIR)/generated \
11011121
$(LINT_SRCS)
11021122

11031123
# clang-format: formatting check (config in .clang-format, dry-run = no changes)

‎assets/cbm-integrations.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"format":1,"product":"codebase-memory-mcp","templates":{"claude_gate":{"posix":{"text":"#!/usr/bin/env bash\n# codebase-memory-mcp search augmenter (Claude Code PreToolUse).\n# NOTE: the legacy filename is kept for zero-migration upgrades.\n# Despite the name this NEVER blocks a tool call - it only adds\n# graph context. Any failure is silent (exit 0, no output).\nBIN={{BIN}}\n[ -x \"$BIN\" ] || exit 0\n\"$BIN\" hook-augment 2>/dev/null\nexit 0\n","bin":"sh"},"windows":{"text":"@echo off\r\nsetlocal DisableDelayedExpansion\r\nREM PreToolUse search and read coverage adapter installed by codebase-memory-mcp.\r\nREM Fail-open: it never blocks or logs hook or prompt content.\r\nset \"BIN={{BIN}}\"\r\nif not exist \"%BIN%\" exit /b 0\r\n\"%BIN%\" hook-augment 2>NUL\r\nexit /b 0\r\n","bin":"cmd"},"released":[{"text":"#!/usr/bin/env bash\n# codebase-memory-mcp search augmenter (Claude Code PreToolUse).\n# NOTE: the legacy filename is kept for zero-migration upgrades.\n# Despite the name this NEVER blocks a tool call - it only adds\n# graph context. Any failure is silent (exit 0, no output).\nBIN=\"{{BIN}}\"\n[ -x \"$BIN\" ] || exit 0\n\"$BIN\" hook-augment 2>/dev/null\nexit 0\n","bin":"raw"}]},"claude_session":{"posix":{"text":"#!/usr/bin/env bash\n# SessionStart context adapter installed by codebase-memory-mcp.\n# Fail-open: it never blocks or logs hook/prompt content.\nBIN={{BIN}}\n[ -x \"$BIN\" ] || exit 0\n\"$BIN\" hook-augment 2>/dev/null\nexit 0\n","bin":"sh"},"windows":{"text":"@echo off\r\nsetlocal DisableDelayedExpansion\r\nREM SessionStart context adapter installed by codebase-memory-mcp.\r\nREM Fail-open: it never blocks or logs hook or prompt content.\r\nset \"BIN={{BIN}}\"\r\nif not exist \"%BIN%\" exit /b 0\r\n\"%BIN%\" hook-augment 2>NUL\r\nexit /b 0\r\n","bin":"cmd"},"released":[{"text":"#!/usr/bin/env bash\n# SessionStart hook: remind agent to use codebase-memory-mcp tools.\n# Installed by codebase-memory-mcp. Fires on startup/resume/clear/compact.\ncat << 'REMINDER'\nCRITICAL - Code Discovery Protocol:\n1. ALWAYS use codebase-memory-mcp tools FIRST for ANY code exploration:\n - search_graph(name_pattern/label/qn_pattern) to find functions/classes/routes\n - trace_path(function_name, mode=calls|data_flow|cross_service) for call chains\n - get_code_snippet(qualified_name) for exact symbol source (precise ranges)\n - query_graph(query) for complex Cypher patterns\n - get_architecture(aspects) for project structure\n - search_code(pattern) for text search (graph-augmented grep)\n2. Use Grep/Glob/Read freely for text, configs, non-code files, and\n always Read a file before editing it.\n3. If a project is not indexed yet, run index_repository FIRST.\nREMINDER\n"}]},"claude_subagent":{"posix":{"text":"#!/usr/bin/env bash\n# SubagentStart context adapter installed by codebase-memory-mcp.\n# Fail-open: it never blocks or logs hook/prompt content.\nBIN={{BIN}}\n[ -x \"$BIN\" ] || exit 0\n\"$BIN\" hook-augment 2>/dev/null\nexit 0\n","bin":"sh"},"windows":{"text":"@echo off\r\nsetlocal DisableDelayedExpansion\r\nREM SubagentStart context adapter installed by codebase-memory-mcp.\r\nREM Fail-open: it never blocks or logs hook or prompt content.\r\nset \"BIN={{BIN}}\"\r\nif not exist \"%BIN%\" exit /b 0\r\n\"%BIN%\" hook-augment 2>NUL\r\nexit /b 0\r\n","bin":"cmd"},"released":[{"text":"#!/usr/bin/env bash\n# SubagentStart hook: tell subagents to use codebase-memory-mcp tools.\n# Installed by codebase-memory-mcp. Fires when any subagent is spawned.\n# SubagentStart injects context via JSON additionalContext, not plain stdout.\ncat << 'REMINDER'\n{\"hookSpecificOutput\":{\"hookEventName\":\"SubagentStart\",\"additionalContext\":\"Code discovery: prefer codebase-memory-mcp tools (search_graph, trace_path, get_code_snippet, query_graph, get_architecture, search_code) over grep/file-read for navigating code. Use Grep/Glob/Read for text, configs, and non-code files.\"}}\nREMINDER\n"}]},"augment_session":{"posix":{"text":"#!/bin/sh\n# SessionStart adapter installed by codebase-memory-mcp.\nBIN={{BIN}}\n[ -x \"$BIN\" ] || exit 0\nexec \"$BIN\" hook-augment --event SessionStart 2>/dev/null\n","bin":"sh"},"windows":{"text":"# SessionStart adapter installed by codebase-memory-mcp.\n$bin = {{BIN}}\nif (-not (Test-Path -LiteralPath $bin -PathType Leaf)) { exit 0 }\n& $bin hook-augment --event SessionStart 2>$null\nexit 0\n","bin":"ps"}},"augment_coverage":{"posix":{"text":"#!/bin/sh\n# PostToolUse view adapter installed by codebase-memory-mcp.\nBIN={{BIN}}\n[ -x \"$BIN\" ] || exit 0\nexec \"$BIN\" hook-augment --dialect augment 2>/dev/null\n","bin":"sh"},"windows":{"text":"# PostToolUse view adapter installed by codebase-memory-mcp.\n$bin = {{BIN}}\nif (-not (Test-Path -LiteralPath $bin -PathType Leaf)) { exit 0 }\n& $bin hook-augment --dialect augment 2>$null\nexit 0\n","bin":"ps"}},"cline_context":{"posix":{"text":"#!/bin/sh\n# Cline {{EVENT}} context adapter installed by codebase-memory-mcp.\nBIN={{BIN}}\n[ -x \"$BIN\" ] || exit 0\nexec \"$BIN\" hook-augment --dialect cline --event {{EVENT}} 2>/dev/null\n","bin":"sh"},"windows":{"text":"# Cline {{EVENT}} context adapter installed by codebase-memory-mcp.\n$bin = {{BIN}}\nif (-not (Test-Path -LiteralPath $bin -PathType Leaf)) { exit 0 }\n& $bin hook-augment --dialect cline --event {{EVENT}} 2>$null\nexit 0\n","bin":"ps"}},"adapter_pi":{"all":{"text":"// Generated by codebase-memory-mcp for pi.\n// Regenerated on install/update from the MCP tool registry, so it cannot\n// drift from the tools the server actually serves. Edits inside this block\n// are overwritten; edit outside it, or remove the markers to take ownership.\nimport { spawn } from 'node:child_process';\n\nconst BIN = '{{BIN}}';\n\nasync function call(tool, args, signal) {\n return new Promise((resolve) => {\n const child = spawn(BIN, ['cli', tool, JSON.stringify(args ?? {})], {\n stdio: ['ignore', 'pipe', 'pipe'],\n env: { ...process.env, CBM_LOG_LEVEL: 'error' },\n });\n let out = '';\n const onAbort = () => { if (!child.killed) child.kill(); };\n signal?.addEventListener('abort', onAbort, { once: true });\n child.stdout.on('data', (d) => (out += d.toString()));\n child.on('error', (e) => {\n signal?.removeEventListener('abort', onAbort);\n resolve({ error: String(e && e.message ? e.message : e) });\n });\n child.on('close', () => {\n signal?.removeEventListener('abort', onAbort);\n const lines = out.split('\\n').map((l) => l.trim()).filter(Boolean);\n for (let i = lines.length - 1; i >= 0; i--) {\n try { return resolve(JSON.parse(lines[i])); } catch { /* keep scanning */ }\n }\n resolve({ error: 'no JSON response from codebase-memory-mcp' });\n });\n });\n}\n\nexport function register(pi) {\n{{TOOLS}}}\n","bin":"js"},"tool_line":" pi.registerTool({ name: '{{TOOL}}', run: (args, ctx) => call('{{TOOL}}', args, ctx?.signal) });\n"},"adapter_opencode":{"all":{"text":"// Generated by codebase-memory-mcp for OpenCode.\n// Regenerated on install/update from the MCP tool registry, so it cannot\n// drift from the tools the server actually serves. Edits inside this block\n// are overwritten; edit outside it, or remove the markers to take ownership.\n// OpenCode already reaches every tool over MCP; this adds only the\n// automatic graph lookup before a grep/glob, which other clients get\n// through their own hook configuration.\nimport { spawn } from 'node:child_process';\n\nconst BIN = '{{BIN}}';\n\nfunction augment(tool, args) {\n return new Promise((resolve) => {\n const child = spawn(BIN, ['hook-augment'], {\n stdio: ['pipe', 'pipe', 'ignore'],\n env: { ...process.env, CBM_LOG_LEVEL: 'error' },\n });\n let out = '';\n child.stdout.on('data', (d) => (out += d.toString()));\n child.on('error', () => resolve(''));\n child.on('close', () => resolve(out));\n child.stdin.end(JSON.stringify({\n hook_event_name: 'PreToolUse',\n tool_name: tool,\n tool_input: args ?? {},\n }));\n });\n}\n\nexport const CodebaseMemory = async () => ({\n 'tool.execute.after': async (input, output) => {\n const tool = input?.tool === 'grep' ? 'Grep' : input?.tool === 'glob' ? 'Glob' : null;\n if (!tool) return;\n const extra = await augment(tool, output?.args);\n if (extra && typeof output?.output === 'string') {\n output.output += '\\n' + extra;\n }\n },\n});\n","bin":"js"}}}}

‎install.ps1‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ $InstallDir = "$env:LOCALAPPDATA\Programs\codebase-memory-mcp"
1616
$BinName = "codebase-memory-mcp.exe"
1717
$WindowsArchiveNames = @(
1818
$BinName,
19+
"cbm-integrations.json",
1920
"LICENSE",
2021
"install.ps1",
2122
"THIRD_PARTY_NOTICES.md"
@@ -322,6 +323,24 @@ if (Test-Path -LiteralPath $DownloadedInstaller -PathType Leaf) {
322323
}
323324
}
324325

326+
# Place the integration-template asset beside the installed binary, mirroring
327+
# install.sh. `install` above published a verified copy to ~/.cbm/assets, but a
328+
# later install/uninstall run from $InstallDir resolves the asset next to the
329+
# binary first; without this copy that lookup misses and the run fails closed
330+
# with "integration assets missing".
331+
$DownloadedAsset = Join-Path $TmpDir "cbm-integrations.json"
332+
if (Test-Path -LiteralPath $DownloadedAsset -PathType Leaf) {
333+
$AssetDest = Join-Path $InstallDir "cbm-integrations.json"
334+
$AssetTmp = "$AssetDest.new"
335+
try {
336+
Copy-Item -LiteralPath $DownloadedAsset -Destination $AssetTmp -Force -ErrorAction Stop
337+
Move-Item -LiteralPath $AssetTmp -Destination $AssetDest -Force -ErrorAction Stop
338+
} catch {
339+
Remove-Item -LiteralPath $AssetTmp -Force -ErrorAction SilentlyContinue
340+
Write-Host "note: could not place cbm-integrations.json in $InstallDir (asset resolves from ~/.cbm/assets)"
341+
}
342+
}
343+
325344
# Verify
326345
try {
327346
$ver = & $Dest --version 2>&1

‎install.sh‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,25 @@ if [ -f "$DL_INSTALLER" ]; then
294294
fi
295295
fi
296296

297+
# Place the integration-template asset beside the installed binary. `install`
298+
# above already published a verified copy to ~/.cbm/assets/<version>/, but a
299+
# later `install`/`uninstall` run from INSTALL_DIR resolves the asset next to
300+
# the binary FIRST — and without this copy that lookup misses, so a re-run or an
301+
# uninstall would fail closed with "integration assets missing" even though a
302+
# valid install just completed. Atomic rename, best effort, same rationale as
303+
# the installer copy above.
304+
DL_ASSET="$DLDIR/cbm-integrations.json"
305+
if [ -f "$DL_ASSET" ]; then
306+
ASSET_TMP="$INSTALL_DIR/.cbm-integrations.json.$$"
307+
if cp "$DL_ASSET" "$ASSET_TMP" 2>/dev/null &&
308+
mv -f "$ASSET_TMP" "$INSTALL_DIR/cbm-integrations.json" 2>/dev/null; then
309+
:
310+
else
311+
rm -f "$ASSET_TMP" 2>/dev/null || true
312+
echo "note: could not place cbm-integrations.json in $INSTALL_DIR (asset resolves from ~/.cbm/assets)"
313+
fi
314+
fi
315+
297316
# Verify
298317
VERSION=$("$DEST" --version 2>&1) || {
299318
echo "error: installed binary failed to run" >&2

‎scripts/build.sh‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,14 @@ else
156156
CFLAGS_EXTRA="$CFLAGS_EXTRA" "${EXTRA_MAKE_ARGS[@]+"${EXTRA_MAKE_ARGS[@]}"}"
157157
fi
158158

159+
# Stage the integration-template asset next to the binary so the dev/CI build
160+
# mirrors the release archive layout. The binary resolves cbm-integrations.json
161+
# next to itself, in $CBM_ASSETS_DIR, or under ~/.cbm/assets/<version>/; a plain
162+
# build/c binary has none of those, so install/uninstall (which render the
163+
# templates to verify hash and ownership) would fail closed exactly as they do
164+
# for a user who deleted the file. Copying it here keeps `install` working
165+
# straight out of a build tree — smoke, local dev, and the release packaging all
166+
# then see the same adjacency.
167+
cp "$ROOT/assets/cbm-integrations.json" "$BUILD_DIR/cbm-integrations.json"
168+
159169
echo "=== Build complete: ${BUILD_DIR}/codebase-memory-mcp ==="

‎scripts/ci/check-binary-composition.sh‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,21 @@ UI_HTTP_NEEDLES=(
9393
'[c]odebase-memory-mcp'
9494
)
9595

96+
# The binary used to embed nine complete shebang'd shell scripts, their
97+
# PowerShell/.cmd twins and two node:child_process client modules — script
98+
# bodies written to disk at 0755, which is precisely the text surface
99+
# Defender's ML scored Trojan:Script/Wacatac.B!ml. They now ship in
100+
# cbm-integrations.json (the binary embeds only its SHA-256); these needles
101+
# prove the removal stayed removed. Source-level enforcement lives in
102+
# tests/test_no_embedded_scripts_contract.sh; this is the shipped-artifact
103+
# half of the same property.
104+
SCRIPT_NEEDLES=(
105+
'#!/usr/bin/env bash'
106+
'#!/bin/bash'
107+
'#!/bin/sh'
108+
'node:child_process'
109+
)
110+
96111
# Canary: proves the needle scan can actually see this file's strings. Without
97112
# it, handing the gate a gzip, a stub or a 0-byte file would pass every
98113
# absence assertion. 168+ occurrences in a real artifact, 0 in anything else.
@@ -367,6 +382,29 @@ check_file() {
367382
printf 'INFO %-22s %s: %d/%d UI/HTTP needles present (no-UI split pending; set CBM_CHECK_UI_ABSENT=1 to enforce)\n' \
368383
A5-no-ui-http "$token" "$ui_hits" "${#UI_HTTP_NEEDLES[@]}"
369384
fi
385+
386+
# A6 — embedded integration-script text. Enforced on standard artifacts:
387+
# every C-source template moved into cbm-integrations.json, so a hit means
388+
# one crept back in. The ui artifact additionally embeds the RAW frontend
389+
# bundle (scripts/embed-frontend.sh stores plain bytes), whose minified JS
390+
# may legitimately contain e.g. "#!" sequences we do not control — so ui
391+
# reports rather than fails until that is measured on a real ui build,
392+
# mirroring A5's honest-INFO pattern. Flip CBM_CHECK_UI_SCRIPTS_ABSENT=1
393+
# to enforce there too.
394+
if [ "$is_ui" -eq 1 ] && [ "${CBM_CHECK_UI_SCRIPTS_ABSENT:-0}" != "1" ]; then
395+
script_hits=0
396+
for needle in "${SCRIPT_NEEDLES[@]}"; do
397+
if LC_ALL=C grep -a -q -F -e "$needle" "$file"; then
398+
script_hits=$((script_hits + 1))
399+
fi
400+
done
401+
printf 'INFO %-22s %s: %d/%d script needles present (raw frontend bundle; set CBM_CHECK_UI_SCRIPTS_ABSENT=1 to enforce)\n' \
402+
A6-no-embedded-scripts "$token" "$script_hits" "${#SCRIPT_NEEDLES[@]}"
403+
else
404+
for needle in "${SCRIPT_NEEDLES[@]}"; do
405+
assert_absent "$file" "$token" A6-no-embedded-scripts "$needle"
406+
done
407+
fi
370408
}
371409

372410
# ── Walk the targets ────────────────────────────────────────────────

‎scripts/gen-integrations-hash.sh‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/bin/sh
2+
# gen-integrations-hash.sh — emit the build-time integrity constant for
3+
# assets/cbm-integrations.json.
4+
#
5+
# The binary embeds exactly ONE fact about the integration templates: the
6+
# SHA-256 of the shipped asset file. Verifying the file against this constant
7+
# before use preserves the tamper-evidence the templates had while they were
8+
# compiled in — moving them out of the binary (AV-surface reduction) must not
9+
# turn them into unauthenticated code the installer happily writes to disk.
10+
#
11+
# Generated, never hand-maintained: a stale constant is exactly the silent
12+
# drift this step exists to prevent, so the header is a build product with a
13+
# real dependency on the JSON file.
14+
set -eu
15+
16+
if [ "$#" -ne 2 ]; then
17+
echo "usage: gen-integrations-hash.sh <asset.json> <out-header.h>" >&2
18+
exit 2
19+
fi
20+
asset="$1"
21+
out="$2"
22+
23+
[ -f "$asset" ] || { echo "gen-integrations-hash: missing $asset" >&2; exit 1; }
24+
25+
if command -v sha256sum >/dev/null 2>&1; then
26+
hash=$(sha256sum "$asset" | cut -d' ' -f1)
27+
elif command -v shasum >/dev/null 2>&1; then
28+
hash=$(shasum -a 256 "$asset" | cut -d' ' -f1)
29+
else
30+
echo "gen-integrations-hash: neither sha256sum nor shasum available" >&2
31+
exit 1
32+
fi
33+
34+
case "$hash" in
35+
*[!0-9a-f]* | "")
36+
echo "gen-integrations-hash: bad digest '$hash'" >&2
37+
exit 1
38+
;;
39+
esac
40+
if [ "${#hash}" -ne 64 ]; then
41+
echo "gen-integrations-hash: bad digest length for '$hash'" >&2
42+
exit 1
43+
fi
44+
45+
mkdir -p "$(dirname "$out")"
46+
tmp="$out.tmp.$$"
47+
{
48+
echo "/* Generated by scripts/gen-integrations-hash.sh — do not edit. */"
49+
echo "#ifndef CBM_INTEGRATIONS_HASH_H"
50+
echo "#define CBM_INTEGRATIONS_HASH_H"
51+
echo "#define CBM_INTEGRATIONS_SHA256 \"$hash\""
52+
echo "#endif"
53+
} >"$tmp"
54+
mv "$tmp" "$out"

0 commit comments

Comments
 (0)