k9guard 1.0.2 → 1.0.4

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/docs/tr/README.md CHANGED
@@ -10,11 +10,14 @@ TypeScript/JavaScript projeleri için kriptografik güvenlik sunan güvenli, haf
10
10
 
11
11
  - **Kriptografik Güvenlik**: NIST SP 800-90A standardına uyumluluk sağlanmıştır
12
12
  - **10 CAPTCHA Türü**: Matematik, metin, dizi, karıştırma, ters çevirme, karma, çok adımlı, görsel, emoji ve özel doğrulama yöntemleri
13
+ - **Adaptif Zorluk**: Kullanıcı başarı oranına göre zorluk seviyesini otomatik ayarlar
13
14
  - **Güvenlik Odaklı**: SHA-256 tuzlu hash algoritması, sunucu taraflı challenge deposu, nonce tabanlı oturum yönetimi ve 5 dakikalık geçerlilik süresi
14
- - **Girdi Doğrulama**: Enjeksiyon saldırılarını önlemek için uzunluk sınırlamaları, tip kontrolü ve sanitizasyon
15
+ - **Tek Kullanımlık Challenge**: Her nonce, `validate()` çağrısında doğru ya da yanlış fark etmeksizin — tüketilir; replay ve brute-force saldırıları engellenir
16
+ - **Katı Yapılandırma**: Geçersiz `type` veya `difficulty` değerleri anında hata fırlatır; sessiz fallback yoktur
17
+ - **Girdi Doğrulama**: Enjeksiyon saldırılarını önlemek için uzunluk sınırlamaları, katı sayısal ayrıştırma, tip kontrolü ve sanitizasyon
15
18
  - **Özel Sorular**: Doğrulama ve sanitizasyon ile kendi sorularınızı tanımlama desteği
16
19
  - **Sıfır Bağımlılık**: Harici bağımlılık gerektirmeyen hafif yapı
17
- - **Kapsamlı Test Edilmiş**: durumlar ve güvenlik senaryoları dahil olmak üzere geniş test kapsama alanı
20
+ - **Kapsamlı Test Edilmiş**: 228+ test ile birim, entegrasyon, güvenlik, durum ve benchmark senaryoları
18
21
  - **OWASP Uyumlu**: OWASP Top 10 güvenlik yönergelerine uygun geliştirme
19
22
  - **Gizlilik Uyumlu**: Kişisel veri saklamayan GDPR/KVKK uyumlu mimari
20
23
 
@@ -190,16 +193,114 @@ const challenge = captcha.generate();
190
193
  const isValid = captcha.validate(challenge, "ankara");
191
194
  ```
192
195
 
196
+ ### Adaptif Zorluk
197
+
198
+ Adaptif zorluk, kullanıcının başarı oranına göre zorluk seviyesini otomatik olarak ayarlar. Bu sayede zorlanan kullanıcılara kolay, hızlı çözen kullanıcılara ise daha zor challenge'lar sunulur.
199
+
200
+ #### Nasıl Çalışır?
201
+
202
+ - Oturum başına son 10 denemeyi takip eder (kayan pencere)
203
+ - **%80+ başarı oranı** — zorluk artar (easy -> medium -> hard)
204
+ - **%40 ve altı başarı oranı** — zorluk düşer (hard -> medium -> easy)
205
+ - **%40-80 arası** — zorluk sabit kalır
206
+ - Herhangi bir ayarlama öncesinde minimum 3 deneme gerekli (histerezis)
207
+ - Oturumlar 30 dakika hareketsizlik sonra otomatik sona erer
208
+ - Maksimum 10.000 eş zamanlı oturum (en eski otomatik temizlenir)
209
+
210
+ #### Seçenek 1: Constructor'da Session ID
211
+
212
+ ```typescript
213
+ const captcha = new K9Guard({
214
+ type: 'math',
215
+ difficulty: 'adaptive',
216
+ sessionId: 'user-123' // herhangi benzersiz string (kullanıcı ID, oturum token'ı, IP vb.)
217
+ });
218
+
219
+ const challenge = captcha.generate(); // user-123'ün mevcut zorluğunu kullanır
220
+ const isValid = captcha.validate(challenge, userAnswer); // sonucu otomatik kaydeder
221
+ ```
222
+
223
+ #### Seçenek 2: Parametre Olarak Session ID
224
+
225
+ ```typescript
226
+ const captcha = new K9Guard({ type: 'math', difficulty: 'adaptive' });
227
+
228
+ // her çağrıda sessionId geçir
229
+ const challenge = captcha.generate('user-123');
230
+ const isValid = captcha.validate(challenge, userAnswer, 'user-123');
231
+ ```
232
+
233
+ #### Seçenek 3: Esnek (Her İkisi)
234
+
235
+ Constructor'daki `sessionId` varsayılan olarak kullanılır. Parametre olarak verilen `sessionId` onu override eder.
236
+
237
+ ```typescript
238
+ const captcha = new K9Guard({
239
+ type: 'math',
240
+ difficulty: 'adaptive',
241
+ sessionId: 'varsayilan-kullanici'
242
+ });
243
+
244
+ captcha.generate(); // 'varsayilan-kullanici' kullanır
245
+ captcha.generate('diger-kullanici'); // 'diger-kullanici' kullanır (override)
246
+ ```
247
+
248
+ #### Oturum Yönetimi
249
+
250
+ ```typescript
251
+ // oturumun mevcut zorluğunu öğren
252
+ const difficulty = captcha.getSessionDifficulty('user-123'); // 'easy' | 'medium' | 'hard' | null
253
+
254
+ // belirli bir oturumu temizle (zorluk 'medium' olarak sıfırlanır)
255
+ captcha.clearSession('user-123');
256
+
257
+ // tüm oturumları temizle
258
+ captcha.clearAllSessions();
259
+ ```
260
+
261
+ #### Tüm CAPTCHA Türleriyle Uyumlu
262
+
263
+ ```typescript
264
+ // adaptif zorluk herhangi bir captcha türüyle çalışır
265
+ const captcha = new K9Guard({ type: 'image', difficulty: 'adaptive', sessionId: 'user-1' });
266
+ const captcha = new K9Guard({ type: 'emoji', difficulty: 'adaptive', sessionId: 'user-1' });
267
+ const captcha = new K9Guard({ type: 'reverse', difficulty: 'adaptive', sessionId: 'user-1' });
268
+ // ... ve diğerleri
269
+ ```
270
+
271
+ #### Express.js Örneği
272
+
273
+ ```typescript
274
+ import express from 'express';
275
+ import K9Guard from 'k9guard';
276
+
277
+ const app = express();
278
+ const captcha = new K9Guard({ type: 'math', difficulty: 'adaptive' });
279
+
280
+ app.get('/captcha', (req, res) => {
281
+ const challenge = captcha.generate(req.sessionID);
282
+ res.json(challenge);
283
+ });
284
+
285
+ app.post('/verify', (req, res) => {
286
+ const isValid = captcha.validate(req.body.challenge, req.body.answer, req.sessionID);
287
+ res.json({ valid: isValid });
288
+ });
289
+ ```
290
+
193
291
  ## API Referansı
194
292
 
195
293
  ### Yapıcı Metod Seçenekleri
196
294
 
295
+ `type` ve `difficulty` alanları **zorunludur** ve katı şekilde doğrulanır. Geçersiz bir değer iletildiğinde constructor anında hata fırlatır.
296
+
197
297
  #### Standart CAPTCHA Seçenekleri
198
298
 
199
299
  ```typescript
200
300
  interface K9GuardOptions {
201
301
  type: 'math' | 'text' | 'sequence' | 'scramble' | 'reverse' | 'mixed' | 'multi' | 'image' | 'emoji';
202
- difficulty: 'easy' | 'medium' | 'hard';
302
+ difficulty: 'easy' | 'medium' | 'hard' | 'adaptive';
303
+ sessionId?: string; // opsiyonel, difficulty 'adaptive' iken gerekli
203
304
  }
204
305
  ```
205
306
 
@@ -209,6 +310,7 @@ interface K9GuardOptions {
209
310
  interface K9GuardCustomOptions {
210
311
  type: 'custom';
211
312
  questions: CustomQuestion[];
313
+ sessionId?: string;
212
314
  }
213
315
 
214
316
  interface CustomQuestion {
@@ -220,10 +322,12 @@ interface CustomQuestion {
220
322
 
221
323
  ### Metotlar
222
324
 
223
- #### `generate(): CaptchaChallenge`
325
+ #### `generate(sessionId?: string): CaptchaChallenge`
224
326
 
225
327
  İstemciye gönderilmesi güvenli bir **public** nesne döndürür — `answer`, `hashedAnswer` ve `salt` çıkarılarak `nonce` ile anahtarlanmış şekilde sunucu tarafında saklanır.
226
328
 
329
+ `difficulty` `'adaptive'` olduğunda, `sessionId` parametresi kullanıcının mevcut zorluk seviyesini belirlemek için kullanılır. Constructor'da `sessionId` verilmişse varsayılan olarak kullanılır.
330
+
227
331
  ```typescript
228
332
  const challenge = captcha.generate();
229
333
  console.log(challenge.question); // kullanıcıya gösterilecek soru
@@ -235,28 +339,112 @@ console.log(challenge.category); // kategori adı (yalnızca type: 'emoji' içi
235
339
  // challenge.answer / .hashedAnswer / .salt — MEVCUT DEĞİL; istemciye hiç gönderilmez
236
340
  ```
237
341
 
238
- #### `validate(challenge: CaptchaChallenge, userInput: string): boolean`
342
+ #### `validate(challenge: CaptchaChallenge, userInput: string, sessionId?: string): boolean`
239
343
 
240
344
  Kullanıcı girdisini `challenge.nonce` üzerinden bulunan sunucu taraflı kayıtla karşılaştırır. Doğruysa `true`, yanlışsa `false` döndürür. Public challenge nesnesindeki `hashedAnswer` veya `salt` değiştirme girişimlerinin hiçbir etkisi yoktur.
241
345
 
346
+ `difficulty` `'adaptive'` olduğunda, doğrulama sonucu otomatik olarak oturuma kaydedilir ve zorluk buna göre ayarlanır.
347
+
348
+ > **⚠️ Tek kullanımlık semantik:** `validate()`, **ilk çağrıda** — cevap doğru ya da yanlış olsun fark etmeksizin — nonce'u tüketir. Her doğrulama denemesinden sonra challenge geçersiz hale gelir. Kullanıcıya yeni bir challenge sunmadan önce mutlaka `generate()` yeniden çağrılmalıdır.
349
+
242
350
  ```typescript
243
351
  const isValid = captcha.validate(challenge, userAnswer);
352
+
353
+ // validate() çağrısından sonra challenge tüketilir.
354
+ // Yeniden deneme için yeni bir challenge üretilmeli:
355
+ if (!isValid) {
356
+ const newChallenge = captcha.generate();
357
+ }
358
+ ```
359
+
360
+ #### `getSessionDifficulty(sessionId: string): Difficulty | null`
361
+
362
+ Oturumun mevcut adaptif zorluk seviyesini döndürür. Instance adaptif modda değilse `null` döndürür.
363
+
364
+ ```typescript
365
+ const difficulty = captcha.getSessionDifficulty('user-123');
366
+ // 'easy' | 'medium' | 'hard' | null
367
+ ```
368
+
369
+ #### `clearSession(sessionId: string): boolean`
370
+
371
+ Belirli bir adaptif oturumu temizler. Oturum mevcutsa `true`, değilse `false` döndürür.
372
+
373
+ ```typescript
374
+ captcha.clearSession('user-123');
375
+ ```
376
+
377
+ #### `clearAllSessions(): void`
378
+
379
+ Tüm adaptif oturumları temizler.
380
+
381
+ ```typescript
382
+ captcha.clearAllSessions();
383
+ ```
384
+
385
+ ### Dışa Aktarılan Yardımcılar
386
+
387
+ ```typescript
388
+ import K9Guard, { AdaptiveTracker, CustomQuestionValidator, CustomQuestionGenerator } from 'k9guard';
389
+ ```
390
+
391
+ | Export | Açıklama |
392
+ |--------|----------|
393
+ | `K9Guard` (varsayılan) | Ana CAPTCHA sınıfı |
394
+ | `AdaptiveTracker` | Bağımsız adaptif zorluk takipçisi (özel entegrasyonlar için) |
395
+ | `CustomQuestionValidator` | Özel soru dizilerini doğrula ve sanitize et |
396
+ | `CustomQuestionGenerator` | Özel soru havuzlarından üretim |
397
+
398
+ ### Tip Exportları
399
+
400
+ ```typescript
401
+ import type {
402
+ K9GuardOptions,
403
+ K9GuardCustomOptions,
404
+ CaptchaChallenge,
405
+ CustomQuestion,
406
+ Difficulty,
407
+ AdaptiveSession,
408
+ AdaptiveAttempt,
409
+ StoredChallenge,
410
+ ImageCaptcha,
411
+ MathCaptcha,
412
+ TextCaptcha,
413
+ SequenceCaptcha,
414
+ ScrambleCaptcha,
415
+ ReverseCaptcha,
416
+ MixedCaptcha,
417
+ CustomCaptcha,
418
+ EmojiCaptcha,
419
+ } from 'k9guard';
244
420
  ```
245
421
 
246
- ## Test Etme
422
+ ## Testler
423
+
424
+ K9Guard, `bun:test` kullanarak 228+ test ile birim, entegrasyon, güvenlik, uç durum ve benchmark senaryolarını kapsar.
247
425
 
248
- Dahil edilen test paketini çalıştırın:
426
+ ### Testleri Çalıştırma
249
427
 
250
428
  ```bash
251
- bun run src/test.ts
429
+ # tüm testleri çalıştır
430
+ bun test
431
+
432
+ # izleme modunda çalıştır
433
+ bun run test:watch
434
+
435
+ # kapsam ile çalıştır
436
+ bun run test:coverage
252
437
  ```
253
438
 
254
- Testler şunları içerir:
255
- - Tüm CAPTCHA türleri için doğru/yanlış/uç durum girdileri
256
- - Özel soru doğrulama senaryoları
257
- - Çok adımlı doğrulamalar
258
- - Girdi sanitizasyonu
259
- - Güvenlik doğrulamaları
439
+ ### Test Kategorileri
440
+
441
+ | Kategori | Kapsam |
442
+ |----------|--------|
443
+ | **Birim** | Her modül bağımsız olarak doğru çıktılar ve uç durumlarla test edilir |
444
+ | **Entegrasyon** | Tüm 10 captcha türü + adaptif mod için tam generate-validate akışı |
445
+ | **Güvenlik** | Timing saldırısı direnci, nonce replay önleme, hash enjeksiyonu, girdi sanitizasyonu, SVG enjeksiyonu |
446
+ | **Uç Durumlar** | Sıfıra bölme, unicode karakterler, eş zamanlı üreteçler, geçersiz girdiler |
447
+ | **Benchmark** | Tüm captcha türleri için performans garantileri (generate < 5ms, validate < 5ms) |
260
448
 
261
449
  ## Katkıda Bulunma
262
450
 
@@ -265,7 +453,7 @@ Katkılarınızı memnuniyetle karşılıyoruz! Nasıl yardımcı olabilirsiniz:
265
453
  1. **Depoyu fork edin**
266
454
  2. **Özellik dalı oluşturun**: `git checkout -b feature/harika-ozellik`
267
455
  3. **Değişiklikleriniz için testler ekleyin**
268
- 4. **Testleri çalıştırın**: `bun run src/test.ts`
456
+ 4. **Testleri çalıştırın**: `bun test`
269
457
  5. **Değişikliklerinizi commit edin**: `git commit -m 'feat: harika özellik eklendi'`
270
458
  6. **Dalınıza push edin**: `git push origin feature/harika-ozellik`
271
459
  7. **Pull Request oluşturun**
package/package.json CHANGED
@@ -1,11 +1,29 @@
1
1
  {
2
2
  "name": "k9guard",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "A secure, lightweight, and flexible CAPTCHA module for TypeScript/JavaScript projects with cryptographic security and multi-language support",
5
- "main": "index.ts",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
6
8
  "type": "module",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ }
20
+ },
7
21
  "scripts": {
8
- "test": "echo \"Error: no test specified\" && exit 1"
22
+ "build": "tsup",
23
+ "test": "bun test",
24
+ "test:watch": "bun test --watch",
25
+ "test:coverage": "bun test --coverage",
26
+ "prepublishOnly": "bun run build"
9
27
  },
10
28
  "keywords": [
11
29
  "captcha",
@@ -42,20 +60,24 @@
42
60
  "url": "https://github.com/K9Crypt/k9guard/issues"
43
61
  },
44
62
  "homepage": "https://github.com/K9Crypt/k9guard#readme",
63
+ "files": [
64
+ "dist/",
65
+ "README.md",
66
+ "LICENSE",
67
+ "docs/"
68
+ ],
45
69
  "engines": {
46
70
  "node": ">=16.0.0"
47
71
  },
48
72
  "devDependencies": {
49
- "@types/bun": "latest",
50
- "@types/node": "^20.19.33",
51
- "@typescript-eslint/eslint-plugin": "^6.21.0",
52
- "@typescript-eslint/parser": "^6.21.0",
53
- "eslint": "^8.57.1",
54
- "tsx": "^4.21.0",
55
- "typescript": "^5.9.3"
56
- },
57
- "peerDependencies": {
58
- "typescript": "^5"
73
+ "@types/bun": "^1.3.14",
74
+ "@types/node": "^25.9.1",
75
+ "@typescript-eslint/eslint-plugin": "^8.59.4",
76
+ "@typescript-eslint/parser": "^8.59.4",
77
+ "eslint": "^10.4.0",
78
+ "tsup": "^8.5.1",
79
+ "tsx": "^4.22.3",
80
+ "typescript": "^6.0.3"
59
81
  },
60
82
  "publishConfig": {
61
83
  "access": "public",
package/index.ts DELETED
@@ -1,4 +0,0 @@
1
- export { K9Guard as default } from './src/K9Guard';
2
- export * from './src/types';
3
- export { CustomQuestionValidator } from './src/validators/customQuestionValidator';
4
- export { CustomQuestionGenerator } from './src/utils/customQuestionGenerator';
package/src/K9Guard.ts DELETED
@@ -1,91 +0,0 @@
1
- import type { K9GuardOptions, K9GuardCustomOptions, CaptchaChallenge, CustomQuestion } from './types';
2
- import { CaptchaGenerator } from './core/captchaGenerator';
3
- import { CaptchaValidator } from './core/captchaValidator';
4
- import { CustomQuestionValidator } from './validators/customQuestionValidator';
5
-
6
- export class K9Guard {
7
- private options: K9GuardOptions | K9GuardCustomOptions;
8
- private generator: CaptchaGenerator;
9
-
10
- constructor(options: K9GuardOptions | K9GuardCustomOptions | { type: 'custom'; questions: CustomQuestion[] } = { type: 'math', difficulty: 'medium' }) {
11
- const processedOptions = this.processOptions(options);
12
- this.generator = new CaptchaGenerator(processedOptions);
13
- this.options = processedOptions;
14
- }
15
-
16
- private processOptions(options: unknown): K9GuardOptions | K9GuardCustomOptions {
17
- if (typeof options !== 'object' || options === null) {
18
- return { type: 'math', difficulty: 'medium' };
19
- }
20
-
21
- const opt = options as Record<string, unknown>;
22
-
23
- if (opt.type === 'custom') {
24
- if (!Array.isArray(opt.questions)) {
25
- throw new Error('Custom type requires questions array');
26
- }
27
-
28
- const validation = CustomQuestionValidator.validate(opt.questions);
29
- if (!validation.valid) {
30
- throw new Error(`Invalid custom questions: ${validation.error}`);
31
- }
32
-
33
- return {
34
- type: 'custom',
35
- questions: CustomQuestionValidator.sanitize(opt.questions as CustomQuestion[])
36
- } as K9GuardCustomOptions;
37
- }
38
-
39
- const validTypes = ['math', 'text', 'sequence', 'scramble', 'reverse', 'mixed', 'multi', 'image', 'emoji'];
40
- const type = validTypes.includes(opt.type as string) ? opt.type : 'math';
41
-
42
- return {
43
- type,
44
- difficulty: opt.difficulty || 'medium'
45
- } as K9GuardOptions;
46
- }
47
-
48
- generate(): CaptchaChallenge {
49
- return this.generator.generate();
50
- }
51
-
52
- validate(challenge: CaptchaChallenge, userInput: string): boolean {
53
- if (!this.isValidChallenge(challenge)) {
54
- return false;
55
- }
56
-
57
- if (typeof userInput !== 'string') {
58
- return false;
59
- }
60
-
61
- // resolve the stored record by nonce; reject if not found or expired.
62
- // hashedAnswer and salt come from the server-side store, never from the client,
63
- // which prevents hash-injection attacks.
64
- const stored = this.generator.lookup(challenge.nonce);
65
- if (!stored) {
66
- return false;
67
- }
68
-
69
- const now = Date.now();
70
- if (now > stored.expiry) {
71
- return false;
72
- }
73
-
74
- return CaptchaValidator.validate(stored, userInput);
75
- }
76
-
77
- private isValidChallenge(challenge: unknown): boolean {
78
- if (typeof challenge !== 'object' || challenge === null) {
79
- return false;
80
- }
81
-
82
- const c = challenge as Record<string, unknown>;
83
-
84
- return (
85
- typeof c.type === 'string' &&
86
- typeof c.question === 'string' &&
87
- typeof c.nonce === 'string' && c.nonce.length > 0 &&
88
- typeof c.expiry === 'number'
89
- );
90
- }
91
- }