chore: Halkyon Learn moves into apps/learn
Its server, client, app and curator, and the tools that write its assets (maps, Anki fixtures, icons), as they are; paths are fixed in the commits that follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
11abd9afce
commit
649499be23
1 changed files with 0 additions and 0 deletions
|
|
@ -0,0 +1,132 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:halkyon_learn_server/src/profile/link_verification.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('a public address', () {
|
||||
test('is one anybody on the internet reaches', () {
|
||||
for (final address in ['93.184.216.34', '1.1.1.1', '2606:4700::1111']) {
|
||||
expect(
|
||||
isPublicAddress(InternetAddress(address)),
|
||||
isTrue,
|
||||
reason: address,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test(
|
||||
'is never the instance itself, its networks, or addresses kept for '
|
||||
"special uses: the instance mustn't be made to look around itself",
|
||||
() {
|
||||
for (final address in [
|
||||
'127.0.0.1', // loopback
|
||||
'0.0.0.0', // this network
|
||||
'10.0.0.1', // private
|
||||
'172.16.5.4',
|
||||
'172.30.80.2', // the instance's edge network, Caddy
|
||||
'192.168.1.1',
|
||||
'169.254.169.254', // link-local, cloud metadata
|
||||
'100.64.0.1', // shared address space (carrier-grade NAT)
|
||||
'192.0.0.8', // IETF protocol assignments
|
||||
'192.0.2.1', // documentation
|
||||
'198.51.100.1',
|
||||
'203.0.113.1',
|
||||
'198.18.0.1', // benchmarking
|
||||
'224.0.0.1', // multicast
|
||||
'240.0.0.1', // reserved
|
||||
'255.255.255.255', // broadcast
|
||||
'::1',
|
||||
'::',
|
||||
'fe80::1',
|
||||
'fc00::1', // unique local
|
||||
'fd12:3456::1',
|
||||
'ff02::1', // multicast
|
||||
'2001:db8::1', // documentation
|
||||
'::ffff:127.0.0.1', // IPv4 inside IPv6
|
||||
'::ffff:10.0.0.1',
|
||||
'64:ff9b::7f00:1', // NAT64 of 127.0.0.1
|
||||
]) {
|
||||
expect(
|
||||
isPublicAddress(InternetAddress(address)),
|
||||
isFalse,
|
||||
reason: address,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('a page links back', () {
|
||||
final profile = Uri.parse('https://halkyon.eu/@Raisondetredev');
|
||||
final page = Uri.parse('https://example.org/about');
|
||||
|
||||
bool linksBack(String html, {Uri? at}) =>
|
||||
linksBackTo(html, pageUrl: at ?? page, profileUrl: profile);
|
||||
|
||||
test('with an <a> or a <link> marked rel="me" to the profile page, its '
|
||||
"host's and its handle's case and a trailing slash aside", () {
|
||||
for (final html in [
|
||||
'<a rel="me" href="https://halkyon.eu/@Raisondetredev">Halkyon</a>',
|
||||
'<head><link rel="me" href="https://halkyon.eu/@raisondetredev/"></head>',
|
||||
'<a href="https://halkyon.eu/@raisondetredev" rel="noopener me">x</a>',
|
||||
"<A REL='Me' HREF='https://HALKYON.EU/@RAISONDETREDEV'>x</A>",
|
||||
'<a rel="me" href="https://halkyon.eu/@raisondetredev?from=site#top">',
|
||||
]) {
|
||||
expect(linksBack(html), isTrue, reason: html);
|
||||
}
|
||||
expect(
|
||||
linksBack(
|
||||
'<a rel="me" href="/@raisondetredev">x</a>',
|
||||
at: Uri.parse('https://halkyon.eu/about'),
|
||||
),
|
||||
isTrue,
|
||||
reason: 'a relative link, from the page it is on',
|
||||
);
|
||||
});
|
||||
|
||||
test('not with a link without rel="me", to another page, another '
|
||||
'person, another host or over plain HTTP', () {
|
||||
for (final html in [
|
||||
'<a href="https://halkyon.eu/@raisondetredev">x</a>',
|
||||
'<a rel="nofollow" href="https://halkyon.eu/@raisondetredev">x</a>',
|
||||
'<a rel="meme" href="https://halkyon.eu/@raisondetredev">x</a>',
|
||||
'<a rel="me" href="https://halkyon.eu/@raisondetredev2">x</a>',
|
||||
'<a rel="me" href="https://halkyon.eu/@raisondetredev/studysets">x</a>',
|
||||
'<a rel="me" href="https://learn.halkyon.eu/@raisondetredev">x</a>',
|
||||
'<a rel="me" href="http://halkyon.eu/@raisondetredev">x</a>',
|
||||
'<p>rel="me" href="https://halkyon.eu/@raisondetredev"</p>',
|
||||
]) {
|
||||
expect(linksBack(html), isFalse, reason: html);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
group('the fetcher refuses, without connecting,', () {
|
||||
final fetcher = SafeLinkFetcher(userAgent: 'test');
|
||||
|
||||
test('what is no HTTPS link on the usual port', () async {
|
||||
for (final link in [
|
||||
'http://example.org/',
|
||||
'ftp://example.org/',
|
||||
'https://example.org:8443/',
|
||||
]) {
|
||||
expect(await fetcher.fetch(Uri.parse(link)), isNull, reason: link);
|
||||
}
|
||||
});
|
||||
|
||||
test(
|
||||
'a host that is, or resolves to, an address that is not public',
|
||||
() async {
|
||||
for (final link in [
|
||||
'https://127.0.0.1/',
|
||||
'https://[::1]/',
|
||||
'https://localhost/',
|
||||
'https://169.254.169.254/latest/meta-data/',
|
||||
]) {
|
||||
expect(await fetcher.fetch(Uri.parse(link)), isNull, reason: link);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue