From 65f6e7bc00b6d34f0fbee33d9be2343f78d9c21e Mon Sep 17 00:00:00 2001
From: Mathieu Piton <27002047+mpiton@users.noreply.github.com>
Date: Fri, 17 Jul 2026 17:04:17 +0200
Subject: [PATCH 1/2] feat(config): declare imgur and flickr credentials in the
manifest
plugin_api.rs already reads imgur_client_id and flickr_api_key via
get_config, but the manifest never declared them, so the host had
nothing to validate or persist. Declare both with empty defaults and
bump to 1.1.0. Two tests pin the contract: manifest_config checks
every key the plugin reads is declared, and the ABI smoke test now
asserts a configured client id reaches the Imgur Client-ID header.
MAT-134
---
CHANGELOG.md | 22 ++++++++++++++++++++++
Cargo.lock | 2 +-
Cargo.toml | 2 +-
plugin.toml | 4 +++-
tests/manifest_config.rs | 14 ++++++++++++++
tests/wasm_smoke.rs | 22 ++++++++++++++++++++--
6 files changed, 61 insertions(+), 5 deletions(-)
create mode 100644 CHANGELOG.md
create mode 100644 tests/manifest_config.rs
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..5810a99
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,22 @@
+# Changelog
+
+All notable changes to vortex-mod-gallery will be documented here.
+Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
+
+## [1.1.0] - 2026-07-17
+
+### Added
+- Declared `imgur_client_id` and `flickr_api_key` in the `[config]` manifest
+ section so the host can validate and persist them; the plugin already reads
+ both through `get_config` (MAT-134 R-01).
+- Manifest test pinning every config key read by `plugin_api.rs` to a
+ `[config]` declaration, and an ABI smoke assertion proving a configured
+ Imgur client id travels from `get_config` into the `Client-ID`
+ Authorization header.
+
+## [1.0.0] - 2026-04-15
+
+### Added
+- Initial release: Imgur, Flickr, and generic HTML gallery extraction with
+ `can_handle`, `supports_playlist`, `extract_links`, `extract_generic`, and
+ `is_http_url` exports.
diff --git a/Cargo.lock b/Cargo.lock
index 6f3fa5b..1898fcd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2238,7 +2238,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "vortex-mod-gallery"
-version = "1.0.0"
+version = "1.1.0"
dependencies = [
"extism",
"extism-pdk",
diff --git a/Cargo.toml b/Cargo.toml
index 918b348..6853a78 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "vortex-mod-gallery"
-version = "1.0.0"
+version = "1.1.0"
edition = "2021"
description = "Gallery WASM plugin for Vortex — Imgur, Reddit, Flickr image galleries"
license = "GPL-3.0"
diff --git a/plugin.toml b/plugin.toml
index f0919ed..20fedb8 100644
--- a/plugin.toml
+++ b/plugin.toml
@@ -1,6 +1,6 @@
[plugin]
name = "vortex-mod-gallery"
-version = "1.0.0"
+version = "1.1.0"
category = "crawler"
author = "vortex-community"
description = "Image galleries: Imgur, Reddit, Flickr, generic scraping"
@@ -15,3 +15,5 @@ http = true
[config]
min_resolution = { type = "string", default = "800x600", description = "Filter by minimum image size (WxH)" }
auto_name = { type = "boolean", default = true, description = "Auto-generate filenames from provider metadata" }
+imgur_client_id = { type = "string", default = "", description = "Imgur API client ID (sent as 'Client-ID' authorization header)" }
+flickr_api_key = { type = "string", default = "", description = "Flickr REST API key" }
diff --git a/tests/manifest_config.rs b/tests/manifest_config.rs
new file mode 100644
index 0000000..ca95aaf
--- /dev/null
+++ b/tests/manifest_config.rs
@@ -0,0 +1,14 @@
+//! Guards the `[config]` contract: keys the plugin reads via `get_config`
+//! must be declared in `plugin.toml` so the host can validate and expose them.
+
+const MANIFEST: &str = include_str!("../plugin.toml");
+
+#[test]
+fn manifest_declares_every_config_key_read_by_the_plugin() {
+ for key in ["min_resolution", "auto_name", "imgur_client_id", "flickr_api_key"] {
+ assert!(
+ MANIFEST.contains(key),
+ "plugin.toml [config] is missing '{key}', which plugin_api.rs reads via get_config"
+ );
+ }
+}
diff --git a/tests/wasm_smoke.rs b/tests/wasm_smoke.rs
index dac6bc1..edfc8f8 100644
--- a/tests/wasm_smoke.rs
+++ b/tests/wasm_smoke.rs
@@ -11,6 +11,7 @@ const GENERIC_URL: &str = "https://example.test/gallery/page.html";
const GENERIC_IMAGE_URL: &str = "https://example.test/gallery/sample.jpg";
const IMGUR_BODY: &str = r#"{"data":[{"id":"img1","title":"sample","link":"https://i.imgur.com/img1.jpg","width":1920,"height":1080}],"status":200,"success":true}"#;
const GENERIC_BODY: &str = r#"
"#;
+const SMOKE_CLIENT_ID: &str = "SMOKE_CLIENT";
fn wasm_path() -> PathBuf {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(WASM_REL_PATH);
@@ -38,6 +39,14 @@ fn stub_http_request() -> Function {
.as_str()
.ok_or_else(|| extism::Error::msg("http_request URL is missing"))?;
let body = if url.contains("api.imgur.com") {
+ // The configured client id must travel from `get_config`
+ // all the way into the Imgur Authorization header.
+ let auth = request["headers"]["Authorization"].as_str().unwrap_or("");
+ if auth != format!("Client-ID {SMOKE_CLIENT_ID}") {
+ return Err(extism::Error::msg(format!(
+ "imgur request carries wrong Authorization header: {auth:?}"
+ )));
+ }
IMGUR_BODY
} else if url == GENERIC_URL {
GENERIC_BODY
@@ -60,8 +69,17 @@ fn stub_get_config() -> Function {
[PTR],
[PTR],
UserData::<()>::default(),
- |plugin, _inputs, outputs, _user_data: UserData<()>| {
- let handle = plugin.memory_new("")?;
+ |plugin, inputs, outputs, _user_data: UserData<()>| {
+ let input = inputs[0]
+ .i64()
+ .ok_or_else(|| extism::Error::msg("get_config expected i64 input"))?;
+ let key: String = plugin.memory_get_val(&Val::I64(input))?;
+ let value = if key == "imgur_client_id" {
+ SMOKE_CLIENT_ID
+ } else {
+ ""
+ };
+ let handle = plugin.memory_new(value)?;
outputs[0] = Val::I64(handle.offset() as i64);
Ok(())
},
From adbf1bcf6c8360ac4cebfb3c06b618f2bde474b8 Mon Sep 17 00:00:00 2001
From: Mathieu Piton <27002047+mpiton@users.noreply.github.com>
Date: Fri, 17 Jul 2026 17:15:05 +0200
Subject: [PATCH 2/2] fix(test): scope manifest key check to the [config]
section
A key mentioned in a description string elsewhere in plugin.toml
could satisfy the old full-file contains check. Match the key as the
left-hand side of a declaration line after the [config] header, and
let rustfmt expand the key array so cargo fmt --check passes.
---
tests/manifest_config.rs | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/tests/manifest_config.rs b/tests/manifest_config.rs
index ca95aaf..217be0c 100644
--- a/tests/manifest_config.rs
+++ b/tests/manifest_config.rs
@@ -5,9 +5,21 @@ const MANIFEST: &str = include_str!("../plugin.toml");
#[test]
fn manifest_declares_every_config_key_read_by_the_plugin() {
- for key in ["min_resolution", "auto_name", "imgur_client_id", "flickr_api_key"] {
+ let (_, config_section) = MANIFEST
+ .split_once("[config]")
+ .expect("plugin.toml must have a [config] section");
+ for key in [
+ "min_resolution",
+ "auto_name",
+ "imgur_client_id",
+ "flickr_api_key",
+ ] {
+ let declared = config_section.lines().any(|line| {
+ line.split_once('=')
+ .is_some_and(|(name, _)| name.trim() == key)
+ });
assert!(
- MANIFEST.contains(key),
+ declared,
"plugin.toml [config] is missing '{key}', which plugin_api.rs reads via get_config"
);
}