pumuki 6.3.192 → 6.3.193
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/core/facts/detectors/text/ios.test.ts +32 -0
- package/core/facts/detectors/text/ios.ts +11 -0
- package/core/facts/extractHeuristicFacts.ts +2 -0
- package/core/rules/presets/heuristics/ios.test.ts +11 -1
- package/core/rules/presets/heuristics/ios.ts +36 -0
- package/docs/codex-skills/ios-enterprise-rules.md +9 -2
- package/integrations/config/skillsDetectorRegistry.ts +12 -0
- package/package.json +1 -1
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
hasSwiftDispatchQueueUsage,
|
|
14
14
|
hasSwiftDispatchSemaphoreUsage,
|
|
15
15
|
hasSwiftAdHocLoggingUsage,
|
|
16
|
+
hasSwiftAlamofireUsage,
|
|
16
17
|
hasSwiftForEachIndicesUsage,
|
|
17
18
|
hasSwiftForceCastUsage,
|
|
18
19
|
hasSwiftFontWeightBoldUsage,
|
|
@@ -43,6 +44,7 @@ import {
|
|
|
43
44
|
hasSwiftSheetIsPresentedUsage,
|
|
44
45
|
hasSwiftScrollViewShowsIndicatorsUsage,
|
|
45
46
|
hasSwiftSensitiveLoggingUsage,
|
|
47
|
+
hasSwiftJSONSerializationUsage,
|
|
46
48
|
hasSwiftStringFormatUsage,
|
|
47
49
|
hasSwiftTabItemUsage,
|
|
48
50
|
hasSwiftTaskDetachedUsage,
|
|
@@ -191,6 +193,36 @@ logger.error("Refresh failed \\(refreshToken)")
|
|
|
191
193
|
assert.equal(hasSwiftSensitiveLoggingUsage(structuredSafe), false);
|
|
192
194
|
});
|
|
193
195
|
|
|
196
|
+
test('detectores iOS de networking y JSON detectan Alamofire y JSONSerialization sin leer comentarios ni strings', () => {
|
|
197
|
+
const source = `
|
|
198
|
+
import Alamofire
|
|
199
|
+
|
|
200
|
+
final class APIClient {
|
|
201
|
+
func load() {
|
|
202
|
+
AF.request("https://example.com")
|
|
203
|
+
let object = try? JSONSerialization.jsonObject(with: Data())
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
`;
|
|
207
|
+
const ignored = `
|
|
208
|
+
import Foundation
|
|
209
|
+
|
|
210
|
+
final class APIClient {
|
|
211
|
+
func load() async throws {
|
|
212
|
+
let url = URL(string: "https://example.com")
|
|
213
|
+
let dto = try JSONDecoder().decode(UserDTO.self, from: Data())
|
|
214
|
+
let text = "JSONSerialization.jsonObject"
|
|
215
|
+
// AF.request("debug")
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
`;
|
|
219
|
+
|
|
220
|
+
assert.equal(hasSwiftAlamofireUsage(source), true);
|
|
221
|
+
assert.equal(hasSwiftJSONSerializationUsage(source), true);
|
|
222
|
+
assert.equal(hasSwiftAlamofireUsage(ignored), false);
|
|
223
|
+
assert.equal(hasSwiftJSONSerializationUsage(ignored), false);
|
|
224
|
+
});
|
|
225
|
+
|
|
194
226
|
test('hasSwiftUncheckedSendableUsage detecta @unchecked Sendable', () => {
|
|
195
227
|
const source = `
|
|
196
228
|
final class LegacyBox: @unchecked Sendable {}
|
|
@@ -464,6 +464,17 @@ export const hasSwiftSensitiveLoggingUsage = (source: string): boolean => {
|
|
|
464
464
|
});
|
|
465
465
|
};
|
|
466
466
|
|
|
467
|
+
export const hasSwiftAlamofireUsage = (source: string): boolean => {
|
|
468
|
+
return (
|
|
469
|
+
collectSwiftRegexLines(source, /^\s*import\s+Alamofire\b/).length > 0 ||
|
|
470
|
+
collectSwiftRegexLines(source, /\b(?:AF|Alamofire)\s*\.\s*request\b/).length > 0
|
|
471
|
+
);
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
export const hasSwiftJSONSerializationUsage = (source: string): boolean => {
|
|
475
|
+
return collectSwiftRegexLines(source, /\bJSONSerialization\s*\./).length > 0;
|
|
476
|
+
};
|
|
477
|
+
|
|
467
478
|
export const hasSwiftUncheckedSendableUsage = (source: string): boolean => {
|
|
468
479
|
return scanCodeLikeSource(source, ({ source: swiftSource, index, current }) => {
|
|
469
480
|
if (current !== '@' || !swiftSource.startsWith('@unchecked', index)) {
|
|
@@ -620,6 +620,8 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
|
|
|
620
620
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftTaskDetachedUsage, ruleId: 'heuristics.ios.task-detached.ast', code: 'HEURISTICS_IOS_TASK_DETACHED_AST', message: 'AST heuristic detected Task.detached usage.' },
|
|
621
621
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftAdHocLoggingUsage, ruleId: 'heuristics.ios.logging.adhoc-print.ast', code: 'HEURISTICS_IOS_LOGGING_ADHOC_PRINT_AST', message: 'AST heuristic detected print/debugPrint/dump/NSLog/os_log usage in iOS production code.' },
|
|
622
622
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftSensitiveLoggingUsage, ruleId: 'heuristics.ios.logging.sensitive-data.ast', code: 'HEURISTICS_IOS_LOGGING_SENSITIVE_DATA_AST', message: 'AST heuristic detected sensitive data in an iOS logging call.' },
|
|
623
|
+
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftAlamofireUsage, ruleId: 'heuristics.ios.networking.alamofire.ast', code: 'HEURISTICS_IOS_NETWORKING_ALAMOFIRE_AST', message: 'AST heuristic detected Alamofire usage in iOS production code; URLSession remains the preferred baseline for new code.' },
|
|
624
|
+
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftJSONSerializationUsage, ruleId: 'heuristics.ios.json.jsonserialization.ast', code: 'HEURISTICS_IOS_JSON_JSONSERIALIZATION_AST', message: 'AST heuristic detected JSONSerialization usage in iOS production code; Codable remains the preferred baseline for new code.' },
|
|
623
625
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftUncheckedSendableUsage, ruleId: 'heuristics.ios.unchecked-sendable.ast', code: 'HEURISTICS_IOS_UNCHECKED_SENDABLE_AST', message: 'AST heuristic detected @unchecked Sendable usage.' },
|
|
624
626
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftPreconcurrencyUsage, ruleId: 'heuristics.ios.preconcurrency.ast', code: 'HEURISTICS_IOS_PRECONCURRENCY_AST', message: 'AST heuristic detected @preconcurrency usage.' },
|
|
625
627
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftNonisolatedUnsafeUsage, ruleId: 'heuristics.ios.nonisolated-unsafe.ast', code: 'HEURISTICS_IOS_NONISOLATED_UNSAFE_AST', message: 'AST heuristic detected nonisolated(unsafe) usage.' },
|
|
@@ -3,7 +3,7 @@ import test from 'node:test';
|
|
|
3
3
|
import { iosRules } from './ios';
|
|
4
4
|
|
|
5
5
|
test('iosRules define reglas heurísticas locked para plataforma ios', () => {
|
|
6
|
-
assert.equal(iosRules.length,
|
|
6
|
+
assert.equal(iosRules.length, 47);
|
|
7
7
|
|
|
8
8
|
const ids = iosRules.map((rule) => rule.id);
|
|
9
9
|
assert.deepEqual(ids, [
|
|
@@ -19,6 +19,8 @@ test('iosRules define reglas heurísticas locked para plataforma ios', () => {
|
|
|
19
19
|
'heuristics.ios.task-detached.ast',
|
|
20
20
|
'heuristics.ios.logging.adhoc-print.ast',
|
|
21
21
|
'heuristics.ios.logging.sensitive-data.ast',
|
|
22
|
+
'heuristics.ios.networking.alamofire.ast',
|
|
23
|
+
'heuristics.ios.json.jsonserialization.ast',
|
|
22
24
|
'heuristics.ios.unchecked-sendable.ast',
|
|
23
25
|
'heuristics.ios.preconcurrency.ast',
|
|
24
26
|
'heuristics.ios.nonisolated-unsafe.ast',
|
|
@@ -71,6 +73,14 @@ test('iosRules define reglas heurísticas locked para plataforma ios', () => {
|
|
|
71
73
|
byId.get('heuristics.ios.logging.sensitive-data.ast')?.then.code,
|
|
72
74
|
'HEURISTICS_IOS_LOGGING_SENSITIVE_DATA_AST'
|
|
73
75
|
);
|
|
76
|
+
assert.equal(
|
|
77
|
+
byId.get('heuristics.ios.networking.alamofire.ast')?.then.code,
|
|
78
|
+
'HEURISTICS_IOS_NETWORKING_ALAMOFIRE_AST'
|
|
79
|
+
);
|
|
80
|
+
assert.equal(
|
|
81
|
+
byId.get('heuristics.ios.json.jsonserialization.ast')?.then.code,
|
|
82
|
+
'HEURISTICS_IOS_JSON_JSONSERIALIZATION_AST'
|
|
83
|
+
);
|
|
74
84
|
assert.equal(
|
|
75
85
|
byId.get('heuristics.ios.preconcurrency.ast')?.then.code,
|
|
76
86
|
'HEURISTICS_IOS_PRECONCURRENCY_AST'
|
|
@@ -217,6 +217,42 @@ export const iosRules: RuleSet = [
|
|
|
217
217
|
code: 'HEURISTICS_IOS_LOGGING_SENSITIVE_DATA_AST',
|
|
218
218
|
},
|
|
219
219
|
},
|
|
220
|
+
{
|
|
221
|
+
id: 'heuristics.ios.networking.alamofire.ast',
|
|
222
|
+
description: 'Detects Alamofire usage in iOS production code; URLSession is the preferred baseline for new code.',
|
|
223
|
+
severity: 'WARN',
|
|
224
|
+
platform: 'ios',
|
|
225
|
+
locked: true,
|
|
226
|
+
when: {
|
|
227
|
+
kind: 'Heuristic',
|
|
228
|
+
where: {
|
|
229
|
+
ruleId: 'heuristics.ios.networking.alamofire.ast',
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
then: {
|
|
233
|
+
kind: 'Finding',
|
|
234
|
+
message: 'AST heuristic detected Alamofire usage in iOS production code; URLSession remains the preferred baseline for new code.',
|
|
235
|
+
code: 'HEURISTICS_IOS_NETWORKING_ALAMOFIRE_AST',
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
id: 'heuristics.ios.json.jsonserialization.ast',
|
|
240
|
+
description: 'Detects JSONSerialization usage in iOS production code; Codable is the preferred baseline for new code.',
|
|
241
|
+
severity: 'WARN',
|
|
242
|
+
platform: 'ios',
|
|
243
|
+
locked: true,
|
|
244
|
+
when: {
|
|
245
|
+
kind: 'Heuristic',
|
|
246
|
+
where: {
|
|
247
|
+
ruleId: 'heuristics.ios.json.jsonserialization.ast',
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
then: {
|
|
251
|
+
kind: 'Finding',
|
|
252
|
+
message: 'AST heuristic detected JSONSerialization usage in iOS production code; Codable remains the preferred baseline for new code.',
|
|
253
|
+
code: 'HEURISTICS_IOS_JSON_JSONSERIALIZATION_AST',
|
|
254
|
+
},
|
|
255
|
+
},
|
|
220
256
|
{
|
|
221
257
|
id: 'heuristics.ios.unchecked-sendable.ast',
|
|
222
258
|
description: 'Detects @unchecked Sendable usage in iOS production code.',
|
|
@@ -499,8 +499,15 @@ struct UseCaseFactory {
|
|
|
499
499
|
✅ **Request/Response interceptors** - Logging, auth tokens
|
|
500
500
|
✅ **SSL pinning** - Para apps con alta seguridad
|
|
501
501
|
✅ **Network reachability** - Detectar conectividad
|
|
502
|
-
|
|
503
|
-
|
|
502
|
+
⚠️ **Alamofire** - No introducir en código nuevo; en brownfield existente se trata como señal de migración gradual. URLSession nativo es la línea base preferente.
|
|
503
|
+
⚠️ **JSONSerialization** - No introducir en código nuevo; en brownfield existente se trata como señal de migración gradual. Codable es la línea base preferente.
|
|
504
|
+
|
|
505
|
+
### Enforcement AST inicial de networking y JSON iOS
|
|
506
|
+
|
|
507
|
+
- `skills.ios.guideline.ios.alamofire-prohibido-usar-urlsession-nativo` se mapea a `heuristics.ios.networking.alamofire.ast`.
|
|
508
|
+
- `skills.ios.guideline.ios.codable-decodificacio-n-automa-tica-de-json-nunca-jsonserialization` se mapea a `heuristics.ios.json.jsonserialization.ast`.
|
|
509
|
+
- `skills.ios.guideline.ios.codable-para-serializacio-n-json-nunca-jsonserialization` se mapea a `heuristics.ios.json.jsonserialization.ast`.
|
|
510
|
+
- En `PROJECT MODE: brownfield`, estos hallazgos son señal de baseline/adopción y deben evitar drift nuevo sin bloquear deuda histórica salvo promoción explícita de policy.
|
|
504
511
|
|
|
505
512
|
```swift
|
|
506
513
|
// ✅ Ejemplo: APIClient con URLSession y async/await
|
|
@@ -55,6 +55,18 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
|
|
|
55
55
|
'ios.logging.sensitive-data',
|
|
56
56
|
['heuristics.ios.logging.sensitive-data.ast']
|
|
57
57
|
),
|
|
58
|
+
'skills.ios.guideline.ios.alamofire-prohibido-usar-urlsession-nativo': heuristicDetector(
|
|
59
|
+
'ios.networking.alamofire',
|
|
60
|
+
['heuristics.ios.networking.alamofire.ast']
|
|
61
|
+
),
|
|
62
|
+
'skills.ios.guideline.ios.codable-decodificacio-n-automa-tica-de-json-nunca-jsonserialization': heuristicDetector(
|
|
63
|
+
'ios.json.jsonserialization',
|
|
64
|
+
['heuristics.ios.json.jsonserialization.ast']
|
|
65
|
+
),
|
|
66
|
+
'skills.ios.guideline.ios.codable-para-serializacio-n-json-nunca-jsonserialization': heuristicDetector(
|
|
67
|
+
'ios.json.jsonserialization',
|
|
68
|
+
['heuristics.ios.json.jsonserialization.ast']
|
|
69
|
+
),
|
|
58
70
|
'skills.ios.no-unchecked-sendable': heuristicDetector('ios.unchecked-sendable', [
|
|
59
71
|
'heuristics.ios.unchecked-sendable.ast',
|
|
60
72
|
]),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.193",
|
|
4
4
|
"description": "Enterprise-grade AST Intelligence System with multi-platform support (iOS, Android, Backend, Frontend) and Feature-First + DDD + Clean Architecture enforcement. Includes dynamic violations API for intelligent querying.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|