ravensight-playtest 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Console noise a developer expects, which is never a finding.
3
+ *
4
+ * Two sources: the built in rule (a 401 on a who am I check is what a
5
+ * signed out page does when it asks "who am I?"; the Snowball round filed it
6
+ * as a bug), and the brief's own `expected_noise` patterns, which live in the
7
+ * local brief (see ../briefLocal.js).
8
+ *
9
+ * A pattern is matched against an automatic console note:
10
+ *
11
+ * - `/.../` is a case insensitive regular expression over the note's text and URL;
12
+ * - otherwise any status codes (`401`) and paths (`/auth/me`) in it must all
13
+ * appear in the note (its status, URL or text), and the other words are a
14
+ * description for the reader, such as "before login";
15
+ * - a pattern with neither is a case insensitive substring of the note's text.
16
+ */
17
+
18
+ /** The built in rule, as the prompt and the README state it. */
19
+ export const BUILT_IN_NOISE = 'a 401 on a who am I check, a request whose last path segment is /me, /whoami, /session or /current_user such as /auth/me, while signed out is not a finding';
20
+
21
+ /**
22
+ * A request whose final path segment asks who the visitor is: `/auth/me`,
23
+ * `/me`, `/whoami`, `/session`, `/current_user`. Only the last segment counts,
24
+ * so `/me/inventory`, `/user/42/save`, `/account/purchase`, `/token` and
25
+ * `/login` are never this rule's: a 401 there can be a real failure.
26
+ */
27
+ const AUTH_CHECK = /\/(?:me|whoami|session|current[_-]?user)\/?(?=$|[?#\s)'",])/i;
28
+
29
+ /**
30
+ * @param {Object} note
31
+ * @returns {string}
32
+ */
33
+ function haystack(note) {
34
+ return `${note && note.text ? note.text : ''} ${note && note.url ? note.url : ''}`;
35
+ }
36
+
37
+ /**
38
+ * Whether a note is the built in case: a 401 on a who am I check.
39
+ *
40
+ * @param {Object} note
41
+ * @returns {boolean}
42
+ */
43
+ export function isAuthCheck401(note) {
44
+ if (!note) return false;
45
+ const is401 = note.status === 401 || /\b401\b/.test(String(note.text || ''));
46
+ return is401 && AUTH_CHECK.test(haystack(note));
47
+ }
48
+
49
+ /**
50
+ * Whether one pattern matches a note.
51
+ *
52
+ * @param {Object} note
53
+ * @param {string} pattern
54
+ * @returns {boolean}
55
+ */
56
+ export function matchesPattern(note, pattern) {
57
+ const value = String(pattern == null ? '' : pattern).trim();
58
+ if (!value || !note) return false;
59
+ const text = haystack(note);
60
+ const regex = value.match(/^\/(.+)\/([a-z]*)$/);
61
+ if (regex) {
62
+ try {
63
+ return new RegExp(regex[1], regex[2].includes('i') ? regex[2] : `${regex[2]}i`).test(text);
64
+ } catch {
65
+ return false;
66
+ }
67
+ }
68
+ const statuses = value.match(/\b[1-5]\d\d\b/g) || [];
69
+ const paths = (value.match(/(?:^|\s)(\/[^\s]*)/g) || []).map(entry => entry.trim().toLowerCase());
70
+ if (statuses.length === 0 && paths.length === 0) return text.toLowerCase().includes(value.toLowerCase());
71
+ const lower = text.toLowerCase();
72
+ return statuses.every(code => note.status === Number(code) || new RegExp(`\\b${code}\\b`).test(text))
73
+ && paths.every(entry => lower.includes(entry));
74
+ }
75
+
76
+ /**
77
+ * Whether an automatic note is expected noise. A persona's own note never is:
78
+ * if the persona saw the game break, that is a finding whatever the console said.
79
+ *
80
+ * @param {Object} note
81
+ * @param {Array<string>} [patterns] the brief's expected_noise
82
+ * @returns {boolean}
83
+ */
84
+ export function isExpectedNoise(note, patterns = []) {
85
+ if (!note || !note.automatic) return false;
86
+ if (isAuthCheck401(note)) return true;
87
+ return (Array.isArray(patterns) ? patterns : []).some(pattern => matchesPattern(note, pattern));
88
+ }
89
+
90
+ /**
91
+ * The expected noise block of the persona prompt.
92
+ *
93
+ * @param {Array<string>} [patterns]
94
+ * @returns {string}
95
+ */
96
+ export function expectedNoiseForPrompt(patterns = []) {
97
+ const lines = [`- built in: ${BUILT_IN_NOISE}`];
98
+ for (const pattern of Array.isArray(patterns) ? patterns : []) if (typeof pattern === 'string' && pattern.trim()) lines.push(`- ${pattern.trim()}`);
99
+ return lines.join('\n');
100
+ }
101
+
102
+ export default { isExpectedNoise, isAuthCheck401, matchesPattern, expectedNoiseForPrompt, BUILT_IN_NOISE };