Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ Version 2.0.33

To be released.

### @fedify/cli

- Fixed `fedify webfinger` ignoring `-p`/`--allow-private-address` and the
Comment thread
coderabbitai[bot] marked this conversation as resolved.
`webfinger.allowPrivateAddress` configuration setting, preventing lookups
against local development servers even with an explicit opt-in. Private
addresses remain blocked by default. [[#1274], [#1276]]

[#1274]: https://github.com/fedify-dev/fedify/issues/1274
[#1276]: https://github.com/fedify-dev/fedify/pull/1276


Version 2.0.32
--------------
Expand Down
9 changes: 9 additions & 0 deletions changes.d/cli/webfinger-private-address.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
links:
'#1274': https://github.com/fedify-dev/fedify/issues/1274
'#1276': https://github.com/fedify-dev/fedify/pull/1276
---
- Fixed `fedify webfinger` ignoring `-p`/`--allow-private-address` and the
`webfinger.allowPrivateAddress` configuration setting, preventing lookups
against local development servers even with an explicit opt-in. Private
addresses remain blocked by default. [[#1274], [#1276]]
9 changes: 7 additions & 2 deletions packages/cli/src/webfinger/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ import { getErrorMessage, NotFoundError } from "./error.ts";
import { convertUrlIfHandle } from "./lib.ts";

export default async function runWebFinger(
{ command: _, resources, ...options }: WebFingerCommand,
{ command: _, resources, allowPrivateAddresses, ...options }:
WebFingerCommand,
) {
await Array.fromAsync(
resources.map((resource) => ({ resource, ...options })),
resources.map((resource) => ({
resource,
...options,
allowPrivateAddress: allowPrivateAddresses,
})),
spinnerWrapper(lookupSingleWebFinger),
);
}
Expand Down
94 changes: 92 additions & 2 deletions packages/cli/src/webfinger/mod.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { clearActiveConfig, setActiveConfig } from "@optique/config";
import { runWithConfig } from "@optique/config/run";
import { parse } from "@optique/core/parser";
import assert from "node:assert/strict";
import process from "node:process";
import test from "node:test";
import { configContext } from "../config.ts";
import { lookupSingleWebFinger } from "./action.ts";
import { type Config, configContext } from "../config.ts";
import runWebFinger, { lookupSingleWebFinger } from "./action.ts";
import { webFingerCommand } from "./command.ts";

const COMMAND = "webfinger";
Expand All @@ -17,6 +19,94 @@ const ALIASES = [
"https://hollo.social/@fedify",
];

for (
const { name, args, config, allowed } of [
{ name: "short flag", args: ["-p"], config: {}, allowed: true },
{
name: "long flag",
args: ["--allow-private-address"],
config: {},
allowed: true,
},
{
name: "configuration opt-in",
args: [],
config: { webfinger: { allowPrivateAddress: true } },
allowed: true,
},
{ name: "default rejection", args: [], config: {}, allowed: false },
{
name: "configuration opt-out",
args: [],
config: { webfinger: { allowPrivateAddress: false } },
allowed: false,
},
{
name: "flag overrides configuration opt-out",
args: ["-p"],
config: { webfinger: { allowPrivateAddress: false } },
allowed: true,
},
] satisfies {
name: string;
args: string[];
config: Config;
allowed: boolean;
}[]
) {
test(`runWebFinger private address - ${name}`, async () => {
const resource = "http://127.0.0.1/users/alice";
const descriptor = { subject: resource, aliases: [resource] };
const requests: { url: string; userAgent: string | null }[] = [];
const originalFetch = globalThis.fetch;
const originalWrite = process.stdout.write;
let output = "";
globalThis.fetch = (input, init) => {
requests.push({
url: String(input),
userAgent: new Headers(init?.headers).get("User-Agent"),
});
return Promise.resolve(
new Response(JSON.stringify(descriptor), {
headers: { "Content-Type": "application/jrd+json" },
}),
);
};
process.stdout.write = function (...args) {
output += String(args[0]);
return Reflect.apply(originalWrite, this, args);
};
try {
const options = await runWithConfig(webFingerCommand, configContext, {
load: () => config,
args: [COMMAND, ...args, "-u", USER_AGENT, resource],
});
await runWebFinger(options);
} finally {
globalThis.fetch = originalFetch;
process.stdout.write = originalWrite;
}
// Assert after execution: lookup and spinner error handling swallow errors
// thrown inside fetch, so assertions in the mock would not fail the test.
assert.deepEqual(
requests,
allowed
? [{
url: "http://127.0.0.1/.well-known/webfinger?resource=" +
encodeURIComponent(resource),
userAgent: USER_AGENT,
}]
: [],
);
if (allowed) {
assert.ok(output.includes(resource), output);
assert.ok(output.includes("subject"), output);
} else {
assert.strictEqual(output, "");
}
});
}

test("Test webFingerCommand - resources only", async () => {
const argsWithResourcesOnly = [COMMAND, ...RESOURCES];
setActiveConfig(configContext.id, {});
Expand Down
Loading