SnowLuma v2.4.2 source snapshot
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run
This commit is contained in:
commit
fa9c73363d
1367 files changed
+355735
No files matched your search
@@ -0,0 +1,145 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { makeDefaultOneBotConfig } from '@snowluma/onebot/config';
|
||||
import {
|
||||
OneBotAccessTokenPolicyError,
|
||||
validateOneBotAccessTokenChanges,
|
||||
} from '../src/webui/onebot-token-policy';
|
||||
|
||||
function validate(
|
||||
previous: ReturnType<typeof makeDefaultOneBotConfig> | null,
|
||||
next: ReturnType<typeof makeDefaultOneBotConfig>,
|
||||
clientIp: string,
|
||||
): void {
|
||||
validateOneBotAccessTokenChanges(previous, next, { clientIp, uin: '10001' });
|
||||
}
|
||||
|
||||
describe('OneBot access token save policy', () => {
|
||||
it('rejects clearing an inbound token from a non-loopback client when the bind host is public', () => {
|
||||
const previous = makeDefaultOneBotConfig();
|
||||
const next = structuredClone(previous);
|
||||
next.networks.httpServers[0].host = '0.0.0.0';
|
||||
next.networks.httpServers[0].accessToken = undefined;
|
||||
|
||||
expect(() => validate(previous, next, '192.0.2.10')).toThrow(OneBotAccessTokenPolicyError);
|
||||
expect(() => validate(previous, next, '192.0.2.10')).toThrow(/令牌/);
|
||||
});
|
||||
|
||||
it('allows a remote client to clear a token when the inbound host is loopback', () => {
|
||||
const previous = makeDefaultOneBotConfig();
|
||||
const next = structuredClone(previous);
|
||||
next.networks.httpServers[0].host = '127.0.0.1';
|
||||
next.networks.httpServers[0].accessToken = undefined;
|
||||
|
||||
expect(() => validate(previous, next, '192.0.2.10')).not.toThrow();
|
||||
});
|
||||
|
||||
it('treats localhost as a loopback bind host for empty inbound tokens', () => {
|
||||
const previous = makeDefaultOneBotConfig();
|
||||
const next = structuredClone(previous);
|
||||
next.networks.wsServers[0].host = 'localhost';
|
||||
next.networks.wsServers[0].accessToken = undefined;
|
||||
|
||||
expect(() => validate(previous, next, '192.0.2.10')).not.toThrow();
|
||||
});
|
||||
|
||||
it('does not treat a missing bind host as loopback', () => {
|
||||
const previous = makeDefaultOneBotConfig();
|
||||
const next = structuredClone(previous);
|
||||
next.networks.httpServers[0].host = undefined;
|
||||
next.networks.httpServers[0].accessToken = undefined;
|
||||
|
||||
expect(() => validate(previous, next, '192.0.2.10')).toThrow(OneBotAccessTokenPolicyError);
|
||||
});
|
||||
|
||||
it('treats every inbound token as new when no readable prior config exists', () => {
|
||||
const next = makeDefaultOneBotConfig();
|
||||
next.networks.httpServers[0].host = '0.0.0.0';
|
||||
next.networks.httpServers[0].accessToken = undefined;
|
||||
|
||||
expect(() => validate(null, next, '192.0.2.10')).toThrow(OneBotAccessTokenPolicyError);
|
||||
});
|
||||
|
||||
it('allows a loopback client to deliberately clear an inbound token', () => {
|
||||
const previous = makeDefaultOneBotConfig();
|
||||
const next = structuredClone(previous);
|
||||
next.networks.wsServers[0].accessToken = undefined;
|
||||
|
||||
expect(() => validate(previous, next, '127.0.0.2')).not.toThrow();
|
||||
});
|
||||
|
||||
it('rejects a newly changed weak inbound token', () => {
|
||||
const previous = makeDefaultOneBotConfig();
|
||||
const next = structuredClone(previous);
|
||||
next.networks.httpServers[0].accessToken = 'SnowLumaSnowLuma';
|
||||
|
||||
expect(() => validate(previous, next, '127.0.0.1')).toThrow(/容易被猜中/);
|
||||
});
|
||||
|
||||
it('does not strand installations whose legacy token is unchanged', () => {
|
||||
const previous = makeDefaultOneBotConfig();
|
||||
previous.networks.httpServers[0].accessToken = 'legacy';
|
||||
const next = structuredClone(previous);
|
||||
next.networks.httpServers[0].name = 'renamed-http';
|
||||
next.networks.httpServers[0].port = 3100;
|
||||
|
||||
expect(() => validate(previous, next, '192.0.2.10')).not.toThrow();
|
||||
});
|
||||
|
||||
it('allows remote edits that leave an existing empty inbound token unchanged', () => {
|
||||
const previous = makeDefaultOneBotConfig();
|
||||
previous.networks.httpServers[0].accessToken = undefined;
|
||||
const next = structuredClone(previous);
|
||||
next.networks.httpServers[0].port = 3100;
|
||||
|
||||
expect(() => validate(previous, next, '192.0.2.10')).not.toThrow();
|
||||
});
|
||||
|
||||
it('preserves legacy tokens when existing endpoints are reordered by name', () => {
|
||||
const previous = makeDefaultOneBotConfig();
|
||||
previous.networks.httpServers[0].accessToken = 'legacy';
|
||||
previous.networks.httpServers.push({
|
||||
...previous.networks.httpServers[0],
|
||||
name: 'second-http',
|
||||
port: 3100,
|
||||
});
|
||||
const next = structuredClone(previous);
|
||||
next.networks.httpServers.reverse();
|
||||
|
||||
expect(() => validate(previous, next, '192.0.2.10')).not.toThrow();
|
||||
});
|
||||
|
||||
it('does not mistake an inserted endpoint for a renamed legacy endpoint', () => {
|
||||
const previous = makeDefaultOneBotConfig();
|
||||
previous.networks.httpServers[0].accessToken = 'legacy';
|
||||
const next = structuredClone(previous);
|
||||
next.networks.httpServers.unshift({
|
||||
...next.networks.httpServers[0],
|
||||
name: 'new-http',
|
||||
port: 3100,
|
||||
});
|
||||
|
||||
expect(() => validate(previous, next, '192.0.2.10')).toThrow(OneBotAccessTokenPolicyError);
|
||||
});
|
||||
|
||||
it('does not impose local inbound-token policy on outbound adapters', () => {
|
||||
const previous = makeDefaultOneBotConfig();
|
||||
const next = structuredClone(previous);
|
||||
next.networks.httpClients.push({
|
||||
name: 'remote-service',
|
||||
url: 'https://example.com/events',
|
||||
accessToken: 'remote-chosen-token',
|
||||
messageFormat: 'array',
|
||||
reportSelfMessage: false,
|
||||
});
|
||||
|
||||
expect(() => validate(previous, next, '192.0.2.10')).not.toThrow();
|
||||
});
|
||||
|
||||
it('accepts a newly changed strong inbound token', () => {
|
||||
const previous = makeDefaultOneBotConfig();
|
||||
const next = structuredClone(previous);
|
||||
next.networks.wsServers[0].accessToken = 'E5xqVb7_9pLt2QwR4M8kZ1nY';
|
||||
|
||||
expect(() => validate(previous, next, '192.0.2.10')).not.toThrow();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user