k9guard 1.0.1 → 1.0.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.
- package/LICENSE +21 -0
- package/README.md +74 -34
- package/docs/tr/README.md +76 -36
- package/package.json +7 -7
- package/src/K9Guard.ts +18 -10
- package/src/core/captchaGenerator.ts +131 -66
- package/src/core/captchaValidator.ts +76 -11
- package/src/types/index.ts +34 -22
- package/src/utils/crypto.ts +83 -250
- package/src/utils/emojiGenerator.ts +88 -0
- package/src/utils/imageGenerator.ts +152 -0
- package/src/utils/reverseGenerator.ts +5 -1
- package/src/utils/scrambleGenerator.ts +25 -13
- package/src/utils/sequenceGenerator.ts +5 -8
- package/src/locale/en.json +0 -46
- package/src/locale/tr.json +0 -46
- package/src/utils/logicGenerator.ts +0 -18
- package/src/utils/riddleBank.ts +0 -18
|
@@ -1,24 +1,35 @@
|
|
|
1
1
|
import { randomBytes } from './crypto';
|
|
2
2
|
|
|
3
3
|
export class ScrambleGenerator {
|
|
4
|
-
private static
|
|
4
|
+
private static readonly easyWords: string[] = [
|
|
5
|
+
'apple', 'cat', 'dog', 'house', 'sun', 'moon', 'car', 'tree', 'book', 'water'
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
private static readonly mediumWords: string[] = [
|
|
5
9
|
'apple', 'cat', 'dog', 'house', 'sun', 'moon', 'car', 'tree', 'book', 'water',
|
|
6
10
|
'bread', 'milk', 'fish', 'bird', 'flower', 'star', 'hand', 'eye', 'nose', 'mouth'
|
|
7
11
|
];
|
|
8
12
|
|
|
13
|
+
// hard pool: longer words with uncommon letter patterns to resist guessing
|
|
14
|
+
private static readonly hardWords: string[] = [
|
|
15
|
+
'typescript', 'javascript', 'algorithm', 'encryption', 'blockchain',
|
|
16
|
+
'metamorphosis', 'cryptography', 'synchronize', 'parallelism', 'obfuscation',
|
|
17
|
+
'infrastructure', 'authentication', 'authorization', 'vulnerability', 'orchestration'
|
|
18
|
+
];
|
|
19
|
+
|
|
9
20
|
static generate(difficulty: 'easy' | 'medium' | 'hard'): { question: string; answer: string } {
|
|
10
|
-
let
|
|
11
|
-
const buffer = randomBytes(4);
|
|
12
|
-
// convert crypto bytes to a float between 0 and 1
|
|
13
|
-
const rand = buffer.readUInt32LE(0) / 0xFFFFFFFF;
|
|
14
|
-
|
|
21
|
+
let pool: string[];
|
|
15
22
|
if (difficulty === 'easy') {
|
|
16
|
-
|
|
23
|
+
pool = this.easyWords;
|
|
17
24
|
} else if (difficulty === 'medium') {
|
|
18
|
-
|
|
25
|
+
pool = this.mediumWords;
|
|
19
26
|
} else {
|
|
20
|
-
|
|
27
|
+
pool = this.hardWords;
|
|
21
28
|
}
|
|
29
|
+
|
|
30
|
+
const buffer = randomBytes(4);
|
|
31
|
+
const rand = buffer.readUInt32LE(0) / 0xFFFFFFFF;
|
|
32
|
+
const word = pool[Math.floor(rand * pool.length)]!;
|
|
22
33
|
|
|
23
34
|
const scrambled = this.scramble(word);
|
|
24
35
|
return { question: scrambled, answer: word };
|
|
@@ -26,10 +37,11 @@ export class ScrambleGenerator {
|
|
|
26
37
|
|
|
27
38
|
private static scramble(word: string): string {
|
|
28
39
|
const arr = word.split('');
|
|
29
|
-
|
|
30
|
-
for
|
|
31
|
-
|
|
32
|
-
|
|
40
|
+
const n = arr.length;
|
|
41
|
+
// batch all uint32 values for Fisher-Yates in one crypto call (n-1 swaps × 4 bytes)
|
|
42
|
+
const batchBuf = randomBytes(n * 4);
|
|
43
|
+
for (let i = n - 1; i > 0; i--) {
|
|
44
|
+
const j = batchBuf.readUInt32LE((n - 1 - i) * 4) % (i + 1);
|
|
33
45
|
[arr[i]!, arr[j]!] = [arr[j]!, arr[i]!];
|
|
34
46
|
}
|
|
35
47
|
return arr.join('');
|
|
@@ -13,16 +13,13 @@ export class SequenceGenerator {
|
|
|
13
13
|
}
|
|
14
14
|
if (difficulty === 'medium') {
|
|
15
15
|
const letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];
|
|
16
|
-
const buffer = randomBytes(4);
|
|
17
|
-
const start = buffer.readUInt32LE(0) % 4;
|
|
18
16
|
const step = 2;
|
|
17
|
+
// max start ensures all 4 elements (start, +2, +4, +6) fit within the 10-letter array
|
|
18
|
+
const maxStart = letters.length - step * 3 - 1;
|
|
19
|
+
const buffer = randomBytes(4);
|
|
20
|
+
const start = buffer.readUInt32LE(0) % (maxStart + 1);
|
|
19
21
|
const sequence = [letters[start], letters[start + step], letters[start + 2 * step]];
|
|
20
|
-
const
|
|
21
|
-
// check if next letter goes out of bounds
|
|
22
|
-
if (nextIndex >= letters.length) {
|
|
23
|
-
return { question: `${sequence.join(', ')}, ?`, answer: '?' };
|
|
24
|
-
}
|
|
25
|
-
const answer = letters[nextIndex]!;
|
|
22
|
+
const answer = letters[start + 3 * step]!;
|
|
26
23
|
return { question: `${sequence.join(', ')}, ?`, answer };
|
|
27
24
|
}
|
|
28
25
|
// hard mode uses fibonacci sequence
|
package/src/locale/en.json
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"riddles": [
|
|
3
|
-
{ "question": "What has keys but can't open locks?", "answer": "piano" },
|
|
4
|
-
{ "question": "What gets wetter as it dries?", "answer": "towel" },
|
|
5
|
-
{ "question": "What has a head, a tail, but no body?", "answer": "coin" },
|
|
6
|
-
{ "question": "What can you catch but not throw?", "answer": "cold" },
|
|
7
|
-
{ "question": "What has one eye but can't see?", "answer": "needle" },
|
|
8
|
-
{ "question": "What is always in front of you but can't be seen?", "answer": "future" },
|
|
9
|
-
{ "question": "What has many teeth but can't bite?", "answer": "comb" },
|
|
10
|
-
{ "question": "What is full of holes but still holds water?", "answer": "sponge" },
|
|
11
|
-
{ "question": "What has a neck but no head?", "answer": "bottle" },
|
|
12
|
-
{ "question": "What can travel around the world while staying in a corner?", "answer": "stamp" },
|
|
13
|
-
{ "question": "What has hands but cannot clap?", "answer": "clock" },
|
|
14
|
-
{ "question": "What runs but never walks?", "answer": "water" },
|
|
15
|
-
{ "question": "What has a bed but never sleeps?", "answer": "river" },
|
|
16
|
-
{ "question": "What has a face and two hands but no arms or legs?", "answer": "clock" },
|
|
17
|
-
{ "question": "What goes up but never comes down?", "answer": "age" },
|
|
18
|
-
{ "question": "What has words but never speaks?", "answer": "book" },
|
|
19
|
-
{ "question": "What has a thumb and four fingers but is not alive?", "answer": "glove" },
|
|
20
|
-
{ "question": "What can fill a room but takes up no space?", "answer": "light" },
|
|
21
|
-
{ "question": "What breaks when you say its name?", "answer": "silence" },
|
|
22
|
-
{ "question": "What has legs but cannot walk?", "answer": "table" }
|
|
23
|
-
],
|
|
24
|
-
"logics": [
|
|
25
|
-
{ "question": "If today is Monday, tomorrow is Tuesday. True or False?", "answer": "True" },
|
|
26
|
-
{ "question": "A cat is an animal. True or False?", "answer": "True" },
|
|
27
|
-
{ "question": "Water is dry. True or False?", "answer": "False" },
|
|
28
|
-
{ "question": "All apples are red. True or False?", "answer": "False" },
|
|
29
|
-
{ "question": "5 is greater than 3. True or False?", "answer": "True" },
|
|
30
|
-
{ "question": "If A > B and B > C, then A > C. True or False?", "answer": "True" },
|
|
31
|
-
{ "question": "A square has 3 sides. True or False?", "answer": "False" },
|
|
32
|
-
{ "question": "The sun rises in the east. True or False?", "answer": "True" },
|
|
33
|
-
{ "question": "Fish can fly. True or False?", "answer": "False" },
|
|
34
|
-
{ "question": "2 + 2 = 4. True or False?", "answer": "True" },
|
|
35
|
-
{ "question": "A triangle has 3 corners. True or False?", "answer": "True" },
|
|
36
|
-
{ "question": "Ice is hot. True or False?", "answer": "False" },
|
|
37
|
-
{ "question": "10 is an even number. True or False?", "answer": "True" },
|
|
38
|
-
{ "question": "Humans need oxygen to breathe. True or False?", "answer": "True" },
|
|
39
|
-
{ "question": "A year has 12 months. True or False?", "answer": "True" },
|
|
40
|
-
{ "question": "Birds are mammals. True or False?", "answer": "False" },
|
|
41
|
-
{ "question": "7 is less than 15. True or False?", "answer": "True" },
|
|
42
|
-
{ "question": "A circle has corners. True or False?", "answer": "False" },
|
|
43
|
-
{ "question": "The earth is flat. True or False?", "answer": "False" },
|
|
44
|
-
{ "question": "A week has 7 days. True or False?", "answer": "True" }
|
|
45
|
-
]
|
|
46
|
-
}
|
package/src/locale/tr.json
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"riddles": [
|
|
3
|
-
{ "question": "Anahtarları var ama kapıları açamaz?", "answer": "piyano" },
|
|
4
|
-
{ "question": "Kurudukça ıslanan nedir?", "answer": "havlu" },
|
|
5
|
-
{ "question": "Baş ve kuyruğu var ama vücudu yok?", "answer": "madeni para" },
|
|
6
|
-
{ "question": "Yakalayabilirsin ama atamazsın?", "answer": "soğuk algınlığı" },
|
|
7
|
-
{ "question": "Bir gözü var ama göremez?", "answer": "iğne" },
|
|
8
|
-
{ "question": "Her zaman önünde ama görülemez?", "answer": "gelecek" },
|
|
9
|
-
{ "question": "Çok dişleri var ama ısırmaz?", "answer": "tarak" },
|
|
10
|
-
{ "question": "Delik dolu ama suyu tutar?", "answer": "sünger" },
|
|
11
|
-
{ "question": "Boynu var ama başı yok?", "answer": "şişe" },
|
|
12
|
-
{ "question": "Dünyayı dolaşır ama köşede kalır?", "answer": "posta pulu" },
|
|
13
|
-
{ "question": "Elleri var ama alkışlayamaz?", "answer": "saat" },
|
|
14
|
-
{ "question": "Koşar ama asla yürümez?", "answer": "su" },
|
|
15
|
-
{ "question": "Yatağı var ama asla uyumaz?", "answer": "nehir" },
|
|
16
|
-
{ "question": "Yüzü ve iki eli var ama kolu ve bacağı yok?", "answer": "saat" },
|
|
17
|
-
{ "question": "Yukarı çıkar ama asla aşağı inmez?", "answer": "yaş" },
|
|
18
|
-
{ "question": "Kelimeleri var ama asla konuşmaz?", "answer": "kitap" },
|
|
19
|
-
{ "question": "Başparmağı ve dört parmağı var ama canlı değil?", "answer": "eldiven" },
|
|
20
|
-
{ "question": "Bir odayı doldurur ama yer kaplamaz?", "answer": "ışık" },
|
|
21
|
-
{ "question": "Adını söylediğinde kırılır?", "answer": "sessizlik" },
|
|
22
|
-
{ "question": "Bacakları var ama yürüyemez?", "answer": "masa" }
|
|
23
|
-
],
|
|
24
|
-
"logics": [
|
|
25
|
-
{ "question": "Bugün Pazartesi ise, yarın Salı'dır. Doğru mu Yanlış mı?", "answer": "Doğru" },
|
|
26
|
-
{ "question": "Kedi bir hayvandır. Doğru mu Yanlış mı?", "answer": "Doğru" },
|
|
27
|
-
{ "question": "Su kurudur. Doğru mu Yanlış mı?", "answer": "Yanlış" },
|
|
28
|
-
{ "question": "Tüm elmalar kırmızıdır. Doğru mu Yanlış mı?", "answer": "Yanlış" },
|
|
29
|
-
{ "question": "5, 3'ten büyüktür. Doğru mu Yanlış mı?", "answer": "Doğru" },
|
|
30
|
-
{ "question": "Eğer A > B ve B > C ise, A > C olur. Doğru mu Yanlış mı?", "answer": "Doğru" },
|
|
31
|
-
{ "question": "Karenin 3 kenarı vardır. Doğru mu Yanlış mı?", "answer": "Yanlış" },
|
|
32
|
-
{ "question": "Güneş doğudan doğar. Doğru mu Yanlış mı?", "answer": "Doğru" },
|
|
33
|
-
{ "question": "Balıklar uçar. Doğru mu Yanlış mı?", "answer": "Yanlış" },
|
|
34
|
-
{ "question": "2 + 2 = 4. Doğru mu Yanlış mı?", "answer": "Doğru" },
|
|
35
|
-
{ "question": "Üçgenin 3 köşesi vardır. Doğru mu Yanlış mı?", "answer": "Doğru" },
|
|
36
|
-
{ "question": "Buz sıcaktır. Doğru mu Yanlış mı?", "answer": "Yanlış" },
|
|
37
|
-
{ "question": "10 çift sayıdır. Doğru mu Yanlış mı?", "answer": "Doğru" },
|
|
38
|
-
{ "question": "İnsanlar nefes almak için oksijene ihtiyaç duyar. Doğru mu Yanlış mı?", "answer": "Doğru" },
|
|
39
|
-
{ "question": "Bir yılda 12 ay vardır. Doğru mu Yanlış mı?", "answer": "Doğru" },
|
|
40
|
-
{ "question": "Kuşlar memelidir. Doğru mu Yanlış mı?", "answer": "Yanlış" },
|
|
41
|
-
{ "question": "7, 15'ten küçüktür. Doğru mu Yanlış mı?", "answer": "Doğru" },
|
|
42
|
-
{ "question": "Dairenin köşeleri vardır. Doğru mu Yanlış mı?", "answer": "Yanlış" },
|
|
43
|
-
{ "question": "Dünya düzdür. Doğru mu Yanlış mı?", "answer": "Yanlış" },
|
|
44
|
-
{ "question": "Bir haftada 7 gün vardır. Doğru mu Yanlış mı?", "answer": "Doğru" }
|
|
45
|
-
]
|
|
46
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { randomBytes } from './crypto';
|
|
2
|
-
import enData from '../locale/en.json';
|
|
3
|
-
import trData from '../locale/tr.json';
|
|
4
|
-
|
|
5
|
-
export class LogicGenerator {
|
|
6
|
-
private static data: Record<'en' | 'tr', { question: string; answer: string }[]> = {
|
|
7
|
-
en: enData.logics,
|
|
8
|
-
tr: trData.logics
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
static getRandom(locale: 'en' | 'tr' = 'en', difficulty: 'easy' | 'medium' | 'hard' = 'easy'): { question: string; answer: string } {
|
|
12
|
-
const logics = this.data[locale];
|
|
13
|
-
// use crypto random to select a logic puzzle securely
|
|
14
|
-
const buffer = randomBytes(4);
|
|
15
|
-
const index = buffer.readUInt32LE(0) % logics.length;
|
|
16
|
-
return logics[index]!;
|
|
17
|
-
}
|
|
18
|
-
}
|
package/src/utils/riddleBank.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { randomBytes } from './crypto';
|
|
2
|
-
import enData from '../locale/en.json';
|
|
3
|
-
import trData from '../locale/tr.json';
|
|
4
|
-
|
|
5
|
-
export class RiddleBank {
|
|
6
|
-
private static data: Record<'en' | 'tr', { question: string; answer: string }[]> = {
|
|
7
|
-
en: enData.riddles,
|
|
8
|
-
tr: trData.riddles
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
static getRandom(locale: 'en' | 'tr' = 'en', difficulty: 'easy' | 'medium' | 'hard' = 'easy'): { question: string; answer: string } {
|
|
12
|
-
const riddles = this.data[locale];
|
|
13
|
-
// use crypto random to select a riddle securely
|
|
14
|
-
const buffer = randomBytes(4);
|
|
15
|
-
const index = buffer.readUInt32LE(0) % riddles.length;
|
|
16
|
-
return riddles[index]!;
|
|
17
|
-
}
|
|
18
|
-
}
|