From d374f4a48c031c9b9fef82611884650f381a26ce Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Aug 2026 15:34:42 +0000 Subject: [PATCH 1/2] Initial plan From fa84670011d432984efba83b370cd917154daa73 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Aug 2026 15:56:01 +0000 Subject: [PATCH 2/2] fix(security): replace mcp/memory with hardened ghcr.io/github/gh-aw-memory image - Add Dockerfile.memory-mcp to build a patched image using node:lts-alpine (fixes CVE-2025-55130 node@22.14.0 and Alpine pkg CVEs) and installing @modelcontextprotocol/server-memory@2026.7.4 (fixes GHSA-w48q-cv73-mx4w sdk@1.0.1 and dependent npm package vulnerabilities) - Add publish-memory-mcp.yml workflow to weekly rebuild/publish ghcr.io/github/gh-aw-memory, parallel to publish-safe-outputs-node.yml - Update server-memory.md to reference ghcr.io/github/gh-aw-memory - Remove stale mcp/memory pin from actions-lock.json and sync derived files - Recompile all workflows so lock files reference ghcr.io/github/gh-aw-memory Closes #49516 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/aw/actions-lock.json | 5 - .github/workflows/mcp-inspector.lock.yml | 14 +- .github/workflows/publish-memory-mcp.yml | 276 ++++++++++++++++++ .github/workflows/shared/mcp/server-memory.md | 2 +- actions/setup/js/Dockerfile.memory-mcp | 45 +++ pkg/actionpins/data/action_pins.json | 5 - pkg/workflow/data/action_pins.json | 5 - 7 files changed, 329 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/publish-memory-mcp.yml create mode 100644 actions/setup/js/Dockerfile.memory-mcp diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json index eade9705cc1..6ee10f93db6 100644 --- a/.github/aw/actions-lock.json +++ b/.github/aw/actions-lock.json @@ -235,11 +235,6 @@ "digest": "sha256:9cb5f26d30b609a1d5560a090371cb9dba84fc32e7df10e5496aa22aa7b52658", "pinned_image": "mcp/markitdown@sha256:9cb5f26d30b609a1d5560a090371cb9dba84fc32e7df10e5496aa22aa7b52658" }, - "mcp/memory": { - "image": "mcp/memory", - "digest": "sha256:db0c2db07a44b6797eba7a832b1bda142ffc899588aae82c92780cbb2252407f", - "pinned_image": "mcp/memory@sha256:db0c2db07a44b6797eba7a832b1bda142ffc899588aae82c92780cbb2252407f" - }, "mcp/notion": { "image": "mcp/notion", "digest": "sha256:df0d6781d03f37bd5b962c85ae1f288382f31b7108c489473641ffc372f43dc9", diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml index ba0f3643aad..0012de2d19a 100644 --- a/.github/workflows/mcp-inspector.lock.yml +++ b/.github/workflows/mcp-inspector.lock.yml @@ -1,5 +1,5 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"7d14124590d3b2ba77c53481e14e290a73790be557f210e9f7b251dce4b89fdc","body_hash":"a66b16f8dc9fea2e6f29f546a5163f80e62b30f827a7792f2a3c1ef9a726e338","agent_id":"copilot","engine_versions":{"copilot":"1.0.77","copilot-sdk":"1.0.8"}} -# gh-aw-manifest: {"version":1,"secrets":["AZURE_CLIENT_ID","AZURE_CLIENT_SECRET","AZURE_TENANT_ID","CONTEXT7_API_KEY","COPILOT_GITHUB_TOKEN","DD_API_KEY","DD_APPLICATION_KEY","DD_APP_KEY","DD_SITE","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN","NOTION_API_TOKEN","SENTRY_ACCESS_TOKEN","SENTRY_OPENAI_API_KEY","SLACK_BOT_TOKEN","TAVILY_API_KEY"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e","version":"v7.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/setup-python","sha":"5fda3b95a4ea91299a34e894583c3862153e4b97","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"astral-sh/setup-uv","sha":"c771a70e6277c0a99b617c7a806ffedaca235ff9","version":"v9.0.0"},{"repo":"docker/build-push-action","sha":"53b7df96c91f9c12dcc8a07bcb9ccacbed38856a","version":"v7.3.0"},{"repo":"docker/setup-buildx-action","sha":"bb05f3f5519dd87d3ba754cc423b652a5edd6d2c","version":"v4.2.0"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.43","digest":"sha256:04e2d1987a565000a8f114b89d806ae7a3864dd4f944be65275b28c93d8690e6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.43@sha256:04e2d1987a565000a8f114b89d806ae7a3864dd4f944be65275b28c93d8690e6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.43","digest":"sha256:d85f57975af5ea23af4996e41ed73fbc8f5b4a47402472bfe82e508f352cb0c1","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.43@sha256:d85f57975af5ea23af4996e41ed73fbc8f5b4a47402472bfe82e508f352cb0c1"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.43","digest":"sha256:26be5e0b8c8f4c41c8a59126b29bb5d80b07253597472ded2a16bdd75abcbf9d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.43@sha256:26be5e0b8c8f4c41c8a59126b29bb5d80b07253597472ded2a16bdd75abcbf9d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.7","digest":"sha256:7545220a9aca134b71e51193ee0eaf4c50756ebf8fbd25a63ae7556e62815c00","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.7@sha256:7545220a9aca134b71e51193ee0eaf4c50756ebf8fbd25a63ae7556e62815c00"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:a8082161d7dceda14b68f32eb39d0eaa96b825d07f5895b096afab9d9e0c7748","pinned_image":"ghcr.io/github/gh-aw-node@sha256:a8082161d7dceda14b68f32eb39d0eaa96b825d07f5895b096afab9d9e0c7748"},{"image":"ghcr.io/github/github-mcp-server:v1.8.0","digest":"sha256:d5a18c04b92714c309eb46a2305087e91a4dbd80420f6e462656699f95093520","pinned_image":"ghcr.io/github/github-mcp-server:v1.8.0@sha256:d5a18c04b92714c309eb46a2305087e91a4dbd80420f6e462656699f95093520"},{"image":"ghcr.io/github/serena-mcp-server:sha-2491b68","digest":"sha256:2fc3fab11db8baca5b4fa760ec52f1010425c97edc8bf4c6437f0cdabd17fcb7","pinned_image":"ghcr.io/github/serena-mcp-server:sha-2491b68@sha256:2fc3fab11db8baca5b4fa760ec52f1010425c97edc8bf4c6437f0cdabd17fcb7"},{"image":"mcp/arxiv-mcp-server","digest":"sha256:6dc6bba6dfed97f4ad6eb8d23a5c98ef5b7fa6184937d54b2d675801cd9dd29e","pinned_image":"mcp/arxiv-mcp-server@sha256:6dc6bba6dfed97f4ad6eb8d23a5c98ef5b7fa6184937d54b2d675801cd9dd29e"},{"image":"mcp/ast-grep:latest","digest":"sha256:5fc3f2e9dcf2c019e92662f608b8d89e12134ed6d91e6f5461de6efd506a1e72","pinned_image":"mcp/ast-grep:latest@sha256:5fc3f2e9dcf2c019e92662f608b8d89e12134ed6d91e6f5461de6efd506a1e72"},{"image":"mcp/context7","digest":"sha256:1174e6a29634a83b2be93ac1fefabf63265f498c02c72201fe3464e687dd8836","pinned_image":"mcp/context7@sha256:1174e6a29634a83b2be93ac1fefabf63265f498c02c72201fe3464e687dd8836"},{"image":"mcp/markitdown","digest":"sha256:9cb5f26d30b609a1d5560a090371cb9dba84fc32e7df10e5496aa22aa7b52658","pinned_image":"mcp/markitdown@sha256:9cb5f26d30b609a1d5560a090371cb9dba84fc32e7df10e5496aa22aa7b52658"},{"image":"mcp/memory","digest":"sha256:db0c2db07a44b6797eba7a832b1bda142ffc899588aae82c92780cbb2252407f","pinned_image":"mcp/memory@sha256:db0c2db07a44b6797eba7a832b1bda142ffc899588aae82c92780cbb2252407f"},{"image":"mcp/notion","digest":"sha256:df0d6781d03f37bd5b962c85ae1f288382f31b7108c489473641ffc372f43dc9","pinned_image":"mcp/notion@sha256:df0d6781d03f37bd5b962c85ae1f288382f31b7108c489473641ffc372f43dc9"},{"image":"node:lts-alpine","digest":"sha256:f70403e87646dc51b45295f4b8b70cdad0b63d2297c4c9899119b03f7af7a6b3","pinned_image":"node:lts-alpine@sha256:f70403e87646dc51b45295f4b8b70cdad0b63d2297c4c9899119b03f7af7a6b3"},{"image":"python:alpine","digest":"sha256:26730869004e2b9c4b9ad09cab8625e81d256d1ce97e72df5520e806b1709f92","pinned_image":"python:alpine@sha256:26730869004e2b9c4b9ad09cab8625e81d256d1ce97e72df5520e806b1709f92"}]} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"94e522ce5650b2e4e124aed61bd867de42cb9816d66fa34de6b413e0b614cbe5","body_hash":"a66b16f8dc9fea2e6f29f546a5163f80e62b30f827a7792f2a3c1ef9a726e338","agent_id":"copilot","engine_versions":{"copilot":"1.0.77","copilot-sdk":"1.0.8"}} +# gh-aw-manifest: {"version":1,"secrets":["AZURE_CLIENT_ID","AZURE_CLIENT_SECRET","AZURE_TENANT_ID","CONTEXT7_API_KEY","COPILOT_GITHUB_TOKEN","DD_API_KEY","DD_APPLICATION_KEY","DD_APP_KEY","DD_SITE","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN","NOTION_API_TOKEN","SENTRY_ACCESS_TOKEN","SENTRY_OPENAI_API_KEY","SLACK_BOT_TOKEN","TAVILY_API_KEY"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e","version":"v7.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/setup-python","sha":"5fda3b95a4ea91299a34e894583c3862153e4b97","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"astral-sh/setup-uv","sha":"c771a70e6277c0a99b617c7a806ffedaca235ff9","version":"v9.0.0"},{"repo":"docker/build-push-action","sha":"53b7df96c91f9c12dcc8a07bcb9ccacbed38856a","version":"v7.3.0"},{"repo":"docker/setup-buildx-action","sha":"bb05f3f5519dd87d3ba754cc423b652a5edd6d2c","version":"v4.2.0"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.43","digest":"sha256:04e2d1987a565000a8f114b89d806ae7a3864dd4f944be65275b28c93d8690e6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.43@sha256:04e2d1987a565000a8f114b89d806ae7a3864dd4f944be65275b28c93d8690e6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.43","digest":"sha256:d85f57975af5ea23af4996e41ed73fbc8f5b4a47402472bfe82e508f352cb0c1","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.43@sha256:d85f57975af5ea23af4996e41ed73fbc8f5b4a47402472bfe82e508f352cb0c1"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.43","digest":"sha256:26be5e0b8c8f4c41c8a59126b29bb5d80b07253597472ded2a16bdd75abcbf9d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.43@sha256:26be5e0b8c8f4c41c8a59126b29bb5d80b07253597472ded2a16bdd75abcbf9d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.7","digest":"sha256:7545220a9aca134b71e51193ee0eaf4c50756ebf8fbd25a63ae7556e62815c00","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.7@sha256:7545220a9aca134b71e51193ee0eaf4c50756ebf8fbd25a63ae7556e62815c00"},{"image":"ghcr.io/github/gh-aw-memory"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:a8082161d7dceda14b68f32eb39d0eaa96b825d07f5895b096afab9d9e0c7748","pinned_image":"ghcr.io/github/gh-aw-node@sha256:a8082161d7dceda14b68f32eb39d0eaa96b825d07f5895b096afab9d9e0c7748"},{"image":"ghcr.io/github/github-mcp-server:v1.8.0","digest":"sha256:d5a18c04b92714c309eb46a2305087e91a4dbd80420f6e462656699f95093520","pinned_image":"ghcr.io/github/github-mcp-server:v1.8.0@sha256:d5a18c04b92714c309eb46a2305087e91a4dbd80420f6e462656699f95093520"},{"image":"ghcr.io/github/serena-mcp-server:sha-2491b68","digest":"sha256:2fc3fab11db8baca5b4fa760ec52f1010425c97edc8bf4c6437f0cdabd17fcb7","pinned_image":"ghcr.io/github/serena-mcp-server:sha-2491b68@sha256:2fc3fab11db8baca5b4fa760ec52f1010425c97edc8bf4c6437f0cdabd17fcb7"},{"image":"mcp/arxiv-mcp-server","digest":"sha256:6dc6bba6dfed97f4ad6eb8d23a5c98ef5b7fa6184937d54b2d675801cd9dd29e","pinned_image":"mcp/arxiv-mcp-server@sha256:6dc6bba6dfed97f4ad6eb8d23a5c98ef5b7fa6184937d54b2d675801cd9dd29e"},{"image":"mcp/ast-grep:latest","digest":"sha256:5fc3f2e9dcf2c019e92662f608b8d89e12134ed6d91e6f5461de6efd506a1e72","pinned_image":"mcp/ast-grep:latest@sha256:5fc3f2e9dcf2c019e92662f608b8d89e12134ed6d91e6f5461de6efd506a1e72"},{"image":"mcp/context7","digest":"sha256:1174e6a29634a83b2be93ac1fefabf63265f498c02c72201fe3464e687dd8836","pinned_image":"mcp/context7@sha256:1174e6a29634a83b2be93ac1fefabf63265f498c02c72201fe3464e687dd8836"},{"image":"mcp/markitdown","digest":"sha256:9cb5f26d30b609a1d5560a090371cb9dba84fc32e7df10e5496aa22aa7b52658","pinned_image":"mcp/markitdown@sha256:9cb5f26d30b609a1d5560a090371cb9dba84fc32e7df10e5496aa22aa7b52658"},{"image":"mcp/notion","digest":"sha256:df0d6781d03f37bd5b962c85ae1f288382f31b7108c489473641ffc372f43dc9","pinned_image":"mcp/notion@sha256:df0d6781d03f37bd5b962c85ae1f288382f31b7108c489473641ffc372f43dc9"},{"image":"node:lts-alpine","digest":"sha256:f70403e87646dc51b45295f4b8b70cdad0b63d2297c4c9899119b03f7af7a6b3","pinned_image":"node:lts-alpine@sha256:f70403e87646dc51b45295f4b8b70cdad0b63d2297c4c9899119b03f7af7a6b3"},{"image":"python:alpine","digest":"sha256:26730869004e2b9c4b9ad09cab8625e81d256d1ce97e72df5520e806b1709f92","pinned_image":"python:alpine@sha256:26730869004e2b9c4b9ad09cab8625e81d256d1ce97e72df5520e806b1709f92"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # # ___ _ _ @@ -90,6 +90,7 @@ # - ghcr.io/github/gh-aw-firewall/api-proxy:0.27.43@sha256:d85f57975af5ea23af4996e41ed73fbc8f5b4a47402472bfe82e508f352cb0c1 # - ghcr.io/github/gh-aw-firewall/squid:0.27.43@sha256:26be5e0b8c8f4c41c8a59126b29bb5d80b07253597472ded2a16bdd75abcbf9d # - ghcr.io/github/gh-aw-mcpg:v0.4.7@sha256:7545220a9aca134b71e51193ee0eaf4c50756ebf8fbd25a63ae7556e62815c00 +# - ghcr.io/github/gh-aw-memory # - ghcr.io/github/gh-aw-node@sha256:a8082161d7dceda14b68f32eb39d0eaa96b825d07f5895b096afab9d9e0c7748 # - ghcr.io/github/github-mcp-server:v1.8.0@sha256:d5a18c04b92714c309eb46a2305087e91a4dbd80420f6e462656699f95093520 # - ghcr.io/github/serena-mcp-server:sha-2491b68@sha256:2fc3fab11db8baca5b4fa760ec52f1010425c97edc8bf4c6437f0cdabd17fcb7 @@ -97,7 +98,6 @@ # - mcp/ast-grep:latest@sha256:5fc3f2e9dcf2c019e92662f608b8d89e12134ed6d91e6f5461de6efd506a1e72 # - mcp/context7@sha256:1174e6a29634a83b2be93ac1fefabf63265f498c02c72201fe3464e687dd8836 # - mcp/markitdown@sha256:9cb5f26d30b609a1d5560a090371cb9dba84fc32e7df10e5496aa22aa7b52658 -# - mcp/memory@sha256:db0c2db07a44b6797eba7a832b1bda142ffc899588aae82c92780cbb2252407f # - mcp/notion@sha256:df0d6781d03f37bd5b962c85ae1f288382f31b7108c489473641ffc372f43dc9 # - node:lts-alpine@sha256:f70403e87646dc51b45295f4b8b70cdad0b63d2297c4c9899119b03f7af7a6b3 # - python:alpine@sha256:26730869004e2b9c4b9ad09cab8625e81d256d1ce97e72df5520e806b1709f92 @@ -674,7 +674,7 @@ jobs: GH_AW_SKILL_DIR: ".github/skills" run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh" - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.43@sha256:04e2d1987a565000a8f114b89d806ae7a3864dd4f944be65275b28c93d8690e6 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.43@sha256:d85f57975af5ea23af4996e41ed73fbc8f5b4a47402472bfe82e508f352cb0c1 ghcr.io/github/gh-aw-firewall/squid:0.27.43@sha256:26be5e0b8c8f4c41c8a59126b29bb5d80b07253597472ded2a16bdd75abcbf9d ghcr.io/github/gh-aw-mcpg:v0.4.7@sha256:7545220a9aca134b71e51193ee0eaf4c50756ebf8fbd25a63ae7556e62815c00 ghcr.io/github/gh-aw-node@sha256:a8082161d7dceda14b68f32eb39d0eaa96b825d07f5895b096afab9d9e0c7748 ghcr.io/github/github-mcp-server:v1.8.0@sha256:d5a18c04b92714c309eb46a2305087e91a4dbd80420f6e462656699f95093520 ghcr.io/github/serena-mcp-server:sha-2491b68@sha256:2fc3fab11db8baca5b4fa760ec52f1010425c97edc8bf4c6437f0cdabd17fcb7 mcp/arxiv-mcp-server@sha256:6dc6bba6dfed97f4ad6eb8d23a5c98ef5b7fa6184937d54b2d675801cd9dd29e mcp/ast-grep:latest@sha256:5fc3f2e9dcf2c019e92662f608b8d89e12134ed6d91e6f5461de6efd506a1e72 mcp/context7@sha256:1174e6a29634a83b2be93ac1fefabf63265f498c02c72201fe3464e687dd8836 mcp/markitdown@sha256:9cb5f26d30b609a1d5560a090371cb9dba84fc32e7df10e5496aa22aa7b52658 mcp/memory@sha256:db0c2db07a44b6797eba7a832b1bda142ffc899588aae82c92780cbb2252407f mcp/notion@sha256:df0d6781d03f37bd5b962c85ae1f288382f31b7108c489473641ffc372f43dc9 node:lts-alpine@sha256:f70403e87646dc51b45295f4b8b70cdad0b63d2297c4c9899119b03f7af7a6b3 python:alpine@sha256:26730869004e2b9c4b9ad09cab8625e81d256d1ce97e72df5520e806b1709f92 + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.43@sha256:04e2d1987a565000a8f114b89d806ae7a3864dd4f944be65275b28c93d8690e6 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.43@sha256:d85f57975af5ea23af4996e41ed73fbc8f5b4a47402472bfe82e508f352cb0c1 ghcr.io/github/gh-aw-firewall/squid:0.27.43@sha256:26be5e0b8c8f4c41c8a59126b29bb5d80b07253597472ded2a16bdd75abcbf9d ghcr.io/github/gh-aw-mcpg:v0.4.7@sha256:7545220a9aca134b71e51193ee0eaf4c50756ebf8fbd25a63ae7556e62815c00 ghcr.io/github/gh-aw-memory ghcr.io/github/gh-aw-node@sha256:a8082161d7dceda14b68f32eb39d0eaa96b825d07f5895b096afab9d9e0c7748 ghcr.io/github/github-mcp-server:v1.8.0@sha256:d5a18c04b92714c309eb46a2305087e91a4dbd80420f6e462656699f95093520 ghcr.io/github/serena-mcp-server:sha-2491b68@sha256:2fc3fab11db8baca5b4fa760ec52f1010425c97edc8bf4c6437f0cdabd17fcb7 mcp/arxiv-mcp-server@sha256:6dc6bba6dfed97f4ad6eb8d23a5c98ef5b7fa6184937d54b2d675801cd9dd29e mcp/ast-grep:latest@sha256:5fc3f2e9dcf2c019e92662f608b8d89e12134ed6d91e6f5461de6efd506a1e72 mcp/context7@sha256:1174e6a29634a83b2be93ac1fefabf63265f498c02c72201fe3464e687dd8836 mcp/markitdown@sha256:9cb5f26d30b609a1d5560a090371cb9dba84fc32e7df10e5496aa22aa7b52658 mcp/notion@sha256:df0d6781d03f37bd5b962c85ae1f288382f31b7108c489473641ffc372f43dc9 node:lts-alpine@sha256:f70403e87646dc51b45295f4b8b70cdad0b63d2297c4c9899119b03f7af7a6b3 python:alpine@sha256:26730869004e2b9c4b9ad09cab8625e81d256d1ce97e72df5520e806b1709f92 - name: Build and install gh-aw CLI from source run: | gh extension remove aw || true @@ -920,7 +920,7 @@ jobs: mkdir -p "$HOME/.copilot" GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_a30f18136c7b9119_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_3c0a477f5b74c6a4_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "agenticworkflows": { @@ -1112,7 +1112,7 @@ jobs: }, "memory": { "type": "stdio", - "container": "mcp/memory", + "container": "ghcr.io/github/gh-aw-memory", "args": [ "-v", "/tmp/gh-aw/cache-memory:/app/dist" @@ -1305,7 +1305,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_a30f18136c7b9119_EOF + GH_AW_MCP_CONFIG_3c0a477f5b74c6a4_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true diff --git a/.github/workflows/publish-memory-mcp.yml b/.github/workflows/publish-memory-mcp.yml new file mode 100644 index 00000000000..20f01912e69 --- /dev/null +++ b/.github/workflows/publish-memory-mcp.yml @@ -0,0 +1,276 @@ +name: Publish memory MCP image + +on: + workflow_dispatch: + schedule: + - cron: '41 4 * * 1' + +permissions: + contents: read + packages: write + +concurrency: + group: publish-memory-mcp + cancel-in-progress: false + +env: + IMAGE_NAME: ghcr.io/github/gh-aw-memory + NODE_IMAGE_TAG: node:lts-alpine + NODE_IMAGE_METADATA_URL: https://hub.docker.com/v2/repositories/library/node/tags/lts-alpine + NODE_IMAGE_COOLDOWN_DAYS: '1' + MCP_MEMORY_VERSION: '2026.7.4' + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v7.0.0 + with: + persist-credentials: false + + - name: Resolve upstream Node image metadata + id: upstream + env: + COOLDOWN_DAYS: ${{ env.NODE_IMAGE_COOLDOWN_DAYS }} + METADATA_URL: ${{ env.NODE_IMAGE_METADATA_URL }} + NODE_IMAGE_TAG: ${{ env.NODE_IMAGE_TAG }} + run: | + set -euo pipefail + + echo "Fetching metadata for ${NODE_IMAGE_TAG} from ${METADATA_URL}" + metadata=$(curl -fsSL "$METADATA_URL") + echo "Metadata fetched successfully" + + digest=$(printf '%s' "$metadata" | jq -r '.digest') + amd64_digest=$(printf '%s' "$metadata" | jq -r '.images[] | select(.os == "linux" and .architecture == "amd64") | .digest' | head -n 1) + arm64_digest=$(printf '%s' "$metadata" | jq -r '.images[] | select(.os == "linux" and .architecture == "arm64") | .digest' | head -n 1) + last_updated=$(printf '%s' "$metadata" | jq -r '.last_updated') + + echo "Resolved multi-platform digest: ${digest}" + echo "Resolved linux/amd64 digest: ${amd64_digest}" + echo "Resolved linux/arm64 digest: ${arm64_digest}" + echo "Upstream last_updated: ${last_updated}" + + if [ -z "$digest" ] || [ "$digest" = "null" ] \ + || [ -z "$amd64_digest" ] || [ "$amd64_digest" = "null" ] \ + || [ -z "$arm64_digest" ] || [ "$arm64_digest" = "null" ] \ + || [ -z "$last_updated" ] || [ "$last_updated" = "null" ]; then + echo "Failed to resolve node:lts-alpine metadata" + exit 1 + fi + + last_updated_epoch=$(date -u -d "$last_updated" +%s) + now_epoch=$(date -u +%s) + cooldown_seconds=$((COOLDOWN_DAYS * 24 * 60 * 60)) + age_seconds=$((now_epoch - last_updated_epoch)) + if [ "$age_seconds" -lt 0 ]; then + age_seconds=0 + fi + + in_cooldown=false + if [ "$age_seconds" -lt "$cooldown_seconds" ]; then + in_cooldown=true + fi + + short_digest=$(printf '%s' "$digest" | sed 's/^sha256://' | cut -c1-12) + + echo "Upstream image age: $((age_seconds / 86400)) day(s) (cooldown window: ${COOLDOWN_DAYS} day(s))" + echo "In cooldown: ${in_cooldown}" + echo "Short digest: ${short_digest}" + + echo "base_image=${NODE_IMAGE_TAG}@${digest}" >> "$GITHUB_OUTPUT" + echo "digest=$digest" >> "$GITHUB_OUTPUT" + echo "short_digest=$short_digest" >> "$GITHUB_OUTPUT" + echo "last_updated=$last_updated" >> "$GITHUB_OUTPUT" + echo "age_days=$((age_seconds / 86400))" >> "$GITHUB_OUTPUT" + echo "in_cooldown=$in_cooldown" >> "$GITHUB_OUTPUT" + + - name: Compute container source hash + id: source + run: | + set -euo pipefail + + dockerfile="actions/setup/js/Dockerfile.memory-mcp" + echo "Computing SHA-256 hash of ${dockerfile}" + hash=$(sha256sum "$dockerfile" | cut -d' ' -f1) + echo "Dockerfile hash: ${hash}" + + echo "hash=$hash" >> "$GITHUB_OUTPUT" + + - name: Log in to GitHub Container Registry + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Inspect published image + id: published + env: + IMAGE_NAME: ${{ env.IMAGE_NAME }} + run: | + set -euo pipefail + + image_exists=false + existing_digest="" + existing_revision="" + existing_dockerfile_hash="" + existing_mcp_memory_version="" + + echo "Attempting to pull ${IMAGE_NAME}:latest" + if docker pull "$IMAGE_NAME:latest" >/dev/null 2>&1; then + image_exists=true + echo "Image pulled successfully; extracting labels" + existing_digest=$(docker inspect "$IMAGE_NAME:latest" --format '{{ index .Config.Labels "org.opencontainers.image.base.digest" }}' 2>/dev/null || true) + existing_revision=$(docker inspect "$IMAGE_NAME:latest" --format '{{ index .Config.Labels "org.opencontainers.image.revision" }}' 2>/dev/null || true) + existing_dockerfile_hash=$(docker inspect "$IMAGE_NAME:latest" --format '{{ index .Config.Labels "io.github.gh-aw.memory.dockerfile-hash" }}' 2>/dev/null || true) + existing_mcp_memory_version=$(docker inspect "$IMAGE_NAME:latest" --format '{{ index .Config.Labels "io.github.gh-aw.memory.server-memory.version" }}' 2>/dev/null || true) + else + echo "Image ${IMAGE_NAME}:latest not found in registry" + fi + + if [ "$existing_digest" = "" ]; then + existing_digest="" + fi + if [ "$existing_revision" = "" ]; then + existing_revision="" + fi + if [ "$existing_dockerfile_hash" = "" ]; then + existing_dockerfile_hash="" + fi + if [ "$existing_mcp_memory_version" = "" ]; then + existing_mcp_memory_version="" + fi + + echo "Image exists: ${image_exists}" + echo "Published base digest: ${existing_digest:-none}" + echo "Published revision: ${existing_revision:-none}" + echo "Published Dockerfile hash: ${existing_dockerfile_hash:-none}" + echo "Published server-memory version: ${existing_mcp_memory_version:-none}" + + echo "image_exists=$image_exists" >> "$GITHUB_OUTPUT" + echo "base_digest=$existing_digest" >> "$GITHUB_OUTPUT" + echo "revision=$existing_revision" >> "$GITHUB_OUTPUT" + echo "dockerfile_hash=$existing_dockerfile_hash" >> "$GITHUB_OUTPUT" + echo "mcp_memory_version=$existing_mcp_memory_version" >> "$GITHUB_OUTPUT" + + - name: Decide whether to publish + id: decision + env: + COOLDOWN_DAYS: ${{ env.NODE_IMAGE_COOLDOWN_DAYS }} + CURRENT_BASE_DIGEST: ${{ steps.upstream.outputs.digest }} + CURRENT_DOCKERFILE_HASH: ${{ steps.source.outputs.hash }} + CURRENT_MCP_MEMORY_VERSION: ${{ env.MCP_MEMORY_VERSION }} + EXISTING_BASE_DIGEST: ${{ steps.published.outputs.base_digest }} + EXISTING_DOCKERFILE_HASH: ${{ steps.published.outputs.dockerfile_hash }} + EXISTING_MCP_MEMORY_VERSION: ${{ steps.published.outputs.mcp_memory_version }} + IMAGE_EXISTS: ${{ steps.published.outputs.image_exists }} + IN_COOLDOWN: ${{ steps.upstream.outputs.in_cooldown }} + run: | + set -euo pipefail + + echo "--- Decision inputs ---" + echo "Image exists: ${IMAGE_EXISTS}" + echo "In cooldown: ${IN_COOLDOWN} (window: ${COOLDOWN_DAYS} day(s))" + echo "Current upstream digest: ${CURRENT_BASE_DIGEST}" + echo "Published base digest: ${EXISTING_BASE_DIGEST:-none}" + echo "Current Dockerfile hash: ${CURRENT_DOCKERFILE_HASH}" + echo "Published Dockerfile hash: ${EXISTING_DOCKERFILE_HASH:-none}" + echo "Current server-memory version: ${CURRENT_MCP_MEMORY_VERSION}" + echo "Published server-memory version: ${EXISTING_MCP_MEMORY_VERSION:-none}" + echo "--- Evaluating conditions ---" + + should_push=false + reason="No changes detected" + + if [ "$IN_COOLDOWN" = "true" ] && { [ "$IMAGE_EXISTS" != "true" ] || [ "$EXISTING_BASE_DIGEST" != "$CURRENT_BASE_DIGEST" ]; }; then + reason="node:lts-alpine is still within the ${COOLDOWN_DAYS}-day cooldown window" + elif [ "$IMAGE_EXISTS" != "true" ]; then + should_push=true + reason="Published image does not exist" + elif [ "$EXISTING_BASE_DIGEST" != "$CURRENT_BASE_DIGEST" ]; then + should_push=true + reason="Upstream node:lts-alpine digest changed" + elif [ "$EXISTING_DOCKERFILE_HASH" != "$CURRENT_DOCKERFILE_HASH" ]; then + should_push=true + reason="Container Dockerfile changed" + elif [ "$EXISTING_MCP_MEMORY_VERSION" != "$CURRENT_MCP_MEMORY_VERSION" ]; then + should_push=true + reason="server-memory version changed to ${CURRENT_MCP_MEMORY_VERSION}" + fi + + echo "Decision: should_push=${should_push}" + echo "Reason: ${reason}" + + echo "should_push=$should_push" >> "$GITHUB_OUTPUT" + echo "reason=$reason" >> "$GITHUB_OUTPUT" + + - name: Set up QEMU + if: steps.decision.outputs.should_push == 'true' + uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 + + - name: Set up Docker Buildx + if: steps.decision.outputs.should_push == 'true' + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 + + - name: Build and push gh-aw-memory + if: steps.decision.outputs.should_push == 'true' + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 + with: + context: . + file: actions/setup/js/Dockerfile.memory-mcp + platforms: linux/amd64,linux/arm64 + pull: true + push: true + build-args: | + NODE_IMAGE=${{ steps.upstream.outputs.base_image }} + NODE_IMAGE_DIGEST=${{ steps.upstream.outputs.digest }} + NODE_IMAGE_UPDATED_AT=${{ steps.upstream.outputs.last_updated }} + DOCKERFILE_HASH=${{ steps.source.outputs.hash }} + MCP_MEMORY_VERSION=${{ env.MCP_MEMORY_VERSION }} + tags: | + ${{ env.IMAGE_NAME }}:latest + ${{ env.IMAGE_NAME }}:node-lts-alpine-${{ steps.upstream.outputs.short_digest }} + labels: | + org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} + org.opencontainers.image.description=Hardened MCP memory server built from node:lts-alpine with patched dependencies + org.opencontainers.image.revision=${{ github.sha }} + org.opencontainers.image.base.name=${{ env.NODE_IMAGE_TAG }} + org.opencontainers.image.base.digest=${{ steps.upstream.outputs.digest }} + io.github.gh-aw.memory.node.updated-at=${{ steps.upstream.outputs.last_updated }} + io.github.gh-aw.memory.dockerfile-hash=${{ steps.source.outputs.hash }} + io.github.gh-aw.memory.server-memory.version=${{ env.MCP_MEMORY_VERSION }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Write summary + if: always() + env: + AGE_DAYS: ${{ steps.upstream.outputs.age_days }} + BASE_DIGEST: ${{ steps.upstream.outputs.digest }} + EXISTING_BASE_DIGEST: ${{ steps.published.outputs.base_digest }} + EXISTING_DOCKERFILE_HASH: ${{ steps.published.outputs.dockerfile_hash }} + IMAGE_EXISTS: ${{ steps.published.outputs.image_exists }} + IN_COOLDOWN: ${{ steps.upstream.outputs.in_cooldown }} + MCP_MEMORY_VERSION: ${{ env.MCP_MEMORY_VERSION }} + REASON: ${{ steps.decision.outputs.reason }} + DOCKERFILE_HASH: ${{ steps.source.outputs.hash }} + SHOULD_PUSH: ${{ steps.decision.outputs.should_push }} + UPDATED_AT: ${{ steps.upstream.outputs.last_updated }} + run: | + { + echo "## gh-aw-memory publish decision" + echo "" + echo "- Published image exists: ${IMAGE_EXISTS}" + echo "- Current node:lts-alpine digest: \`${BASE_DIGEST}\`" + echo "- Published base digest: \`${EXISTING_BASE_DIGEST:-none}\`" + echo "- Current Dockerfile hash: \`${DOCKERFILE_HASH}\`" + echo "- Published Dockerfile hash: \`${EXISTING_DOCKERFILE_HASH:-none}\`" + echo "- server-memory version: \`${MCP_MEMORY_VERSION}\`" + echo "- Upstream last updated: ${UPDATED_AT}" + echo "- Upstream age: ${AGE_DAYS} day(s)" + echo "- In cooldown: ${IN_COOLDOWN}" + echo "- Push image: ${SHOULD_PUSH}" + echo "- Reason: ${REASON}" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/shared/mcp/server-memory.md b/.github/workflows/shared/mcp/server-memory.md index a3355e1e359..20a8220fbbc 100644 --- a/.github/workflows/shared/mcp/server-memory.md +++ b/.github/workflows/shared/mcp/server-memory.md @@ -4,7 +4,7 @@ mcp-servers: memory: - container: "mcp/memory" + container: "ghcr.io/github/gh-aw-memory" args: - "-v" - "/tmp/gh-aw/cache-memory:/app/dist" diff --git a/actions/setup/js/Dockerfile.memory-mcp b/actions/setup/js/Dockerfile.memory-mcp new file mode 100644 index 00000000000..6367459249b --- /dev/null +++ b/actions/setup/js/Dockerfile.memory-mcp @@ -0,0 +1,45 @@ +# syntax=docker/dockerfile:1.7 + +ARG NODE_IMAGE=node:lts-alpine +FROM ${NODE_IMAGE} + +ARG NODE_IMAGE +ARG NODE_IMAGE_DIGEST="" +ARG NODE_IMAGE_UPDATED_AT="" +ARG DOCKERFILE_HASH="" +ARG NPM_VERSION=11.19.0 +ARG MCP_MEMORY_VERSION=2026.7.4 + +WORKDIR /app + +# Intentional: upgrade all packages to pick up security fixes; downstream digest pins the result. +# After upgrading npm, patch its bundled dependencies to meet minimum safe versions +# (tar >= 7.5.22 for GHSA-23hp-3jrh-7fpw, brace-expansion >= 5.0.8 for GHSA-mh99-v99m-4gvg). +RUN apk upgrade --no-cache \ + && apk info -v | sort \ + && npm install --global "npm@${NPM_VERSION}" \ + && npm install --prefix "$(npm root -g)/npm" --no-save "tar@^7.5.22" "brace-expansion@^5.0.8" \ + && npm cache clean --force + +# Install @modelcontextprotocol/server-memory from npm. +# Using a pinned version ensures the image is reproducible and picks up +# a fully-patched @modelcontextprotocol/sdk (>= 1.24.0). +RUN npm install "@modelcontextprotocol/server-memory@${MCP_MEMORY_VERSION}" \ + && npm cache clean --force + +# Persist memory to /app/dist/ so the existing Docker volume mount +# (-v /tmp/gh-aw/cache-memory:/app/dist) continues to work. +RUN mkdir -p /app/dist +ENV MEMORY_FILE_PATH=/app/dist/memory.json +ENV NODE_ENV=production + +LABEL org.opencontainers.image.source="https://github.com/github/gh-aw" \ + org.opencontainers.image.description="Hardened MCP memory server built from node:lts-alpine with patched dependencies" \ + org.opencontainers.image.base.name="${NODE_IMAGE}" \ + org.opencontainers.image.base.digest="${NODE_IMAGE_DIGEST}" \ + io.github.gh-aw.memory.node.updated-at="${NODE_IMAGE_UPDATED_AT}" \ + io.github.gh-aw.memory.npm.version="${NPM_VERSION}" \ + io.github.gh-aw.memory.server-memory.version="${MCP_MEMORY_VERSION}" \ + io.github.gh-aw.memory.dockerfile-hash="${DOCKERFILE_HASH}" + +ENTRYPOINT ["node", "node_modules/@modelcontextprotocol/server-memory/dist/index.js"] diff --git a/pkg/actionpins/data/action_pins.json b/pkg/actionpins/data/action_pins.json index eade9705cc1..6ee10f93db6 100644 --- a/pkg/actionpins/data/action_pins.json +++ b/pkg/actionpins/data/action_pins.json @@ -235,11 +235,6 @@ "digest": "sha256:9cb5f26d30b609a1d5560a090371cb9dba84fc32e7df10e5496aa22aa7b52658", "pinned_image": "mcp/markitdown@sha256:9cb5f26d30b609a1d5560a090371cb9dba84fc32e7df10e5496aa22aa7b52658" }, - "mcp/memory": { - "image": "mcp/memory", - "digest": "sha256:db0c2db07a44b6797eba7a832b1bda142ffc899588aae82c92780cbb2252407f", - "pinned_image": "mcp/memory@sha256:db0c2db07a44b6797eba7a832b1bda142ffc899588aae82c92780cbb2252407f" - }, "mcp/notion": { "image": "mcp/notion", "digest": "sha256:df0d6781d03f37bd5b962c85ae1f288382f31b7108c489473641ffc372f43dc9", diff --git a/pkg/workflow/data/action_pins.json b/pkg/workflow/data/action_pins.json index eade9705cc1..6ee10f93db6 100644 --- a/pkg/workflow/data/action_pins.json +++ b/pkg/workflow/data/action_pins.json @@ -235,11 +235,6 @@ "digest": "sha256:9cb5f26d30b609a1d5560a090371cb9dba84fc32e7df10e5496aa22aa7b52658", "pinned_image": "mcp/markitdown@sha256:9cb5f26d30b609a1d5560a090371cb9dba84fc32e7df10e5496aa22aa7b52658" }, - "mcp/memory": { - "image": "mcp/memory", - "digest": "sha256:db0c2db07a44b6797eba7a832b1bda142ffc899588aae82c92780cbb2252407f", - "pinned_image": "mcp/memory@sha256:db0c2db07a44b6797eba7a832b1bda142ffc899588aae82c92780cbb2252407f" - }, "mcp/notion": { "image": "mcp/notion", "digest": "sha256:df0d6781d03f37bd5b962c85ae1f288382f31b7108c489473641ffc372f43dc9",