pumuki 6.3.208 → 6.3.210
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 +57 -0
- package/core/facts/detectors/text/ios.ts +25 -0
- package/core/facts/extractHeuristicFacts.ts +2 -0
- package/core/rules/presets/heuristics/ios.ts +38 -0
- package/integrations/config/skillsDetectorRegistry.ts +16 -0
- package/integrations/config/skillsMarkdownRules.ts +18 -0
- package/package.json +1 -1
- package/skills.lock.json +6 -42
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
hasSwiftLegacyExpectationDescriptionUsage,
|
|
30
30
|
hasSwiftLegacySwiftUiObservableWrapperUsage,
|
|
31
31
|
hasSwiftMainThreadBlockingSleepUsage,
|
|
32
|
+
hasSwiftMassiveViewControllerResponsibilityUsage,
|
|
32
33
|
hasSwiftMixedTestingFrameworksUsage,
|
|
33
34
|
hasSwiftLegacyXCTestImportUsage,
|
|
34
35
|
hasSwiftModernizableXCTestSuiteUsage,
|
|
@@ -44,6 +45,7 @@ import {
|
|
|
44
45
|
hasSwiftOnTapGestureUsage,
|
|
45
46
|
hasSwiftOperationQueueUsage,
|
|
46
47
|
hasSwiftContainsUserFilterUsage,
|
|
48
|
+
hasSwiftCustomSingletonUsage,
|
|
47
49
|
hasSwiftPassedValueStateWrapperUsage,
|
|
48
50
|
hasSwiftPhysicalTextAlignmentUsage,
|
|
49
51
|
hasSwiftPreconcurrencyUsage,
|
|
@@ -260,6 +262,61 @@ final class CartViewModel {
|
|
|
260
262
|
assert.equal(hasSwiftStrongSelfEscapingClosureUsage(source), false);
|
|
261
263
|
});
|
|
262
264
|
|
|
265
|
+
test('hasSwiftCustomSingletonUsage detecta singletons propios y excluye usos de singletons del sistema', () => {
|
|
266
|
+
const source = `
|
|
267
|
+
final class SessionStore {
|
|
268
|
+
static let shared = SessionStore()
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
final class MutableStore {
|
|
272
|
+
public static var shared: MutableStore = MutableStore()
|
|
273
|
+
}
|
|
274
|
+
`;
|
|
275
|
+
const ignored = `
|
|
276
|
+
final class APIClient {
|
|
277
|
+
let session = URLSession.shared
|
|
278
|
+
let text = "static let shared = SessionStore()"
|
|
279
|
+
// static let shared = SessionStore()
|
|
280
|
+
}
|
|
281
|
+
`;
|
|
282
|
+
|
|
283
|
+
assert.equal(hasSwiftCustomSingletonUsage(source), true);
|
|
284
|
+
assert.equal(hasSwiftCustomSingletonUsage(ignored), false);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test('hasSwiftMassiveViewControllerResponsibilityUsage detecta ViewControllers con acceso directo a infraestructura', () => {
|
|
288
|
+
const source = `
|
|
289
|
+
final class CheckoutViewController: UIViewController {
|
|
290
|
+
override func viewDidLoad() {
|
|
291
|
+
super.viewDidLoad()
|
|
292
|
+
URLSession.shared.dataTask(with: URL(string: "https://example.com")!)
|
|
293
|
+
UserDefaults.standard.set(true, forKey: "seen")
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
`;
|
|
297
|
+
const ignored = `
|
|
298
|
+
final class CheckoutViewController: UIViewController {
|
|
299
|
+
private let viewModel: CheckoutViewModel
|
|
300
|
+
|
|
301
|
+
init(viewModel: CheckoutViewModel) {
|
|
302
|
+
self.viewModel = viewModel
|
|
303
|
+
super.init(nibName: nil, bundle: nil)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
override func viewDidLoad() {
|
|
307
|
+
super.viewDidLoad()
|
|
308
|
+
viewModel.load()
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
let text = "URLSession.shared.dataTask"
|
|
313
|
+
// UserDefaults.standard.set(true, forKey: "seen")
|
|
314
|
+
`;
|
|
315
|
+
|
|
316
|
+
assert.equal(hasSwiftMassiveViewControllerResponsibilityUsage(source), true);
|
|
317
|
+
assert.equal(hasSwiftMassiveViewControllerResponsibilityUsage(ignored), false);
|
|
318
|
+
});
|
|
319
|
+
|
|
263
320
|
test('detectores de logging iOS detectan logs ad-hoc y PII en produccion', () => {
|
|
264
321
|
const adHoc = `
|
|
265
322
|
print(user.id)
|
|
@@ -519,6 +519,31 @@ export const hasSwiftStrongSelfEscapingClosureUsage = (source: string): boolean
|
|
|
519
519
|
return false;
|
|
520
520
|
};
|
|
521
521
|
|
|
522
|
+
export const hasSwiftCustomSingletonUsage = (source: string): boolean => {
|
|
523
|
+
const singletonDeclarationPattern =
|
|
524
|
+
/^\s*(?:(?:private|fileprivate|internal|public|open)\s+)?static\s+(?:let|var)\s+shared\b(?:\s*:\s*[A-Za-z_][A-Za-z0-9_.<>]*)?\s*=/;
|
|
525
|
+
return source.split(/\r?\n/).some((line) => {
|
|
526
|
+
const sanitizedLine = stripSwiftLineForSemanticScan(line);
|
|
527
|
+
return singletonDeclarationPattern.test(sanitizedLine);
|
|
528
|
+
});
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
export const hasSwiftMassiveViewControllerResponsibilityUsage = (source: string): boolean => {
|
|
532
|
+
const sanitized = sanitizeSwiftSourceForMultilineRegex(source);
|
|
533
|
+
const viewControllerPattern =
|
|
534
|
+
/\bclass\s+[A-Za-z_][A-Za-z0-9_]*\s*:\s*(?:[A-Za-z_][A-Za-z0-9_]*\s*,\s*)*UIViewController\b[\s\S]{0,8000}?\n\}/g;
|
|
535
|
+
const infrastructurePattern =
|
|
536
|
+
/\b(?:URLSession\s*\.\s*shared|JSONSerialization|UserDefaults\s*\.\s*standard|NSManagedObjectContext|NSPersistentContainer|NSFetchRequest|FileManager\s*\.\s*default)\b/;
|
|
537
|
+
|
|
538
|
+
for (const match of sanitized.matchAll(viewControllerPattern)) {
|
|
539
|
+
const body = match[0] ?? '';
|
|
540
|
+
if (infrastructurePattern.test(body)) {
|
|
541
|
+
return true;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return false;
|
|
545
|
+
};
|
|
546
|
+
|
|
522
547
|
export const hasSwiftAdHocLoggingUsage = (source: string): boolean => {
|
|
523
548
|
return collectSwiftRegexLines(
|
|
524
549
|
source,
|
|
@@ -648,6 +648,8 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
|
|
|
648
648
|
{ 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.' },
|
|
649
649
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftStrongDelegateReferenceUsage, ruleId: 'heuristics.ios.memory.strong-delegate.ast', code: 'HEURISTICS_IOS_MEMORY_STRONG_DELEGATE_AST', message: 'AST heuristic detected a strong delegate/dataSource reference; weak delegates remain the preferred baseline to avoid retain cycles.' },
|
|
650
650
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftStrongSelfEscapingClosureUsage, ruleId: 'heuristics.ios.memory.strong-self-escaping-closure.ast', code: 'HEURISTICS_IOS_MEMORY_STRONG_SELF_ESCAPING_CLOSURE_AST', message: 'AST heuristic detected strong self capture in an escaping iOS closure; weak or unowned captures remain the preferred baseline when ownership is not explicit.' },
|
|
651
|
+
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftCustomSingletonUsage, ruleId: 'heuristics.ios.architecture.custom-singleton.ast', code: 'HEURISTICS_IOS_ARCHITECTURE_CUSTOM_SINGLETON_AST', message: 'AST heuristic detected a custom static shared singleton in iOS production code; dependency injection remains the preferred baseline for app-owned services.' },
|
|
652
|
+
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftMassiveViewControllerResponsibilityUsage, ruleId: 'heuristics.ios.architecture.massive-view-controller.ast', code: 'HEURISTICS_IOS_ARCHITECTURE_MASSIVE_VIEW_CONTROLLER_AST', message: 'AST heuristic detected a UIViewController with direct infrastructure/data access; move data access behind application/domain boundaries.' },
|
|
651
653
|
{ 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.' },
|
|
652
654
|
{ 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.' },
|
|
653
655
|
{ 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.' },
|
|
@@ -219,6 +219,44 @@ export const iosRules: RuleSet = [
|
|
|
219
219
|
code: 'HEURISTICS_IOS_MEMORY_STRONG_SELF_ESCAPING_CLOSURE_AST',
|
|
220
220
|
},
|
|
221
221
|
},
|
|
222
|
+
{
|
|
223
|
+
id: 'heuristics.ios.architecture.custom-singleton.ast',
|
|
224
|
+
description: 'Detects custom static shared singletons in iOS production code.',
|
|
225
|
+
severity: 'WARN',
|
|
226
|
+
platform: 'ios',
|
|
227
|
+
locked: true,
|
|
228
|
+
when: {
|
|
229
|
+
kind: 'Heuristic',
|
|
230
|
+
where: {
|
|
231
|
+
ruleId: 'heuristics.ios.architecture.custom-singleton.ast',
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
then: {
|
|
235
|
+
kind: 'Finding',
|
|
236
|
+
message:
|
|
237
|
+
'AST heuristic detected a custom static shared singleton in iOS production code; dependency injection remains the preferred baseline for app-owned services.',
|
|
238
|
+
code: 'HEURISTICS_IOS_ARCHITECTURE_CUSTOM_SINGLETON_AST',
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
id: 'heuristics.ios.architecture.massive-view-controller.ast',
|
|
243
|
+
description: 'Detects UIViewController classes with direct infrastructure/data access.',
|
|
244
|
+
severity: 'WARN',
|
|
245
|
+
platform: 'ios',
|
|
246
|
+
locked: true,
|
|
247
|
+
when: {
|
|
248
|
+
kind: 'Heuristic',
|
|
249
|
+
where: {
|
|
250
|
+
ruleId: 'heuristics.ios.architecture.massive-view-controller.ast',
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
then: {
|
|
254
|
+
kind: 'Finding',
|
|
255
|
+
message:
|
|
256
|
+
'AST heuristic detected a UIViewController with direct infrastructure/data access; move data access behind application/domain boundaries.',
|
|
257
|
+
code: 'HEURISTICS_IOS_ARCHITECTURE_MASSIVE_VIEW_CONTROLLER_AST',
|
|
258
|
+
},
|
|
259
|
+
},
|
|
222
260
|
{
|
|
223
261
|
id: 'heuristics.ios.logging.adhoc-print.ast',
|
|
224
262
|
description: 'Detects print/debugPrint/dump/NSLog/os_log usage in iOS production code.',
|
|
@@ -58,6 +58,22 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
|
|
|
58
58
|
'heuristics.ios.memory.strong-self-escaping-closure.ast',
|
|
59
59
|
]
|
|
60
60
|
),
|
|
61
|
+
'skills.ios.guideline.ios.no-singleton-usar-inyeccio-n-de-dependencias-no-compartir-instancias-g': heuristicDetector(
|
|
62
|
+
'ios.architecture.custom-singleton',
|
|
63
|
+
['heuristics.ios.architecture.custom-singleton.ast']
|
|
64
|
+
),
|
|
65
|
+
'skills.ios.guideline.ios.no-singletons-excepto-sistema-urlsession-shared-esta-ok': heuristicDetector(
|
|
66
|
+
'ios.architecture.custom-singleton',
|
|
67
|
+
['heuristics.ios.architecture.custom-singleton.ast']
|
|
68
|
+
),
|
|
69
|
+
'skills.ios.guideline.ios.massive-view-controllers-viewcontrollers-que-mezclan-presentacio-n-nav': heuristicDetector(
|
|
70
|
+
'ios.architecture.massive-view-controller',
|
|
71
|
+
['heuristics.ios.architecture.massive-view-controller.ast']
|
|
72
|
+
),
|
|
73
|
+
'skills.ios.guideline.ios.mvc-evitar-massive-view-controller-no-escalable': heuristicDetector(
|
|
74
|
+
'ios.architecture.massive-view-controller',
|
|
75
|
+
['heuristics.ios.architecture.massive-view-controller.ast']
|
|
76
|
+
),
|
|
61
77
|
'skills.ios.guideline.ios.prohibido-print-y-logs-ad-hoc': heuristicDetector(
|
|
62
78
|
'ios.logging.adhoc-print',
|
|
63
79
|
['heuristics.ios.logging.adhoc-print.ast']
|
|
@@ -435,6 +435,24 @@ const normalizeKnownRuleTarget = (
|
|
|
435
435
|
) {
|
|
436
436
|
return 'skills.ios.guideline.ios.evitar-retain-cycles-especialmente-en-closures-delegates';
|
|
437
437
|
}
|
|
438
|
+
if (
|
|
439
|
+
includes('no singleton') ||
|
|
440
|
+
includes('no singletons') ||
|
|
441
|
+
includes('static shared') ||
|
|
442
|
+
includes('static let shared') ||
|
|
443
|
+
includes('static var shared')
|
|
444
|
+
) {
|
|
445
|
+
return 'skills.ios.guideline.ios.no-singleton-usar-inyeccio-n-de-dependencias-no-compartir-instancias-g';
|
|
446
|
+
}
|
|
447
|
+
if (
|
|
448
|
+
includes('massive view controller') ||
|
|
449
|
+
includes('massive view controllers') ||
|
|
450
|
+
includes('viewcontrollers que mezclan') ||
|
|
451
|
+
includes('viewcontroller que mezclan') ||
|
|
452
|
+
(includes('mvc') && includes('evitar'))
|
|
453
|
+
) {
|
|
454
|
+
return 'skills.ios.guideline.ios.massive-view-controllers-viewcontrollers-que-mezclan-presentacio-n-nav';
|
|
455
|
+
}
|
|
438
456
|
if (
|
|
439
457
|
includes('mixing legacy xctest style') ||
|
|
440
458
|
includes('mixed xctest and swift testing') ||
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.210",
|
|
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": {
|
package/skills.lock.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "1.0",
|
|
3
3
|
"compilerVersion": "1.0.0",
|
|
4
|
-
"generatedAt": "2026-05-13T12:
|
|
4
|
+
"generatedAt": "2026-05-13T12:51:35.774Z",
|
|
5
5
|
"bundles": [
|
|
6
6
|
{
|
|
7
7
|
"name": "android-guidelines",
|
|
@@ -5764,7 +5764,7 @@
|
|
|
5764
5764
|
"name": "ios-guidelines",
|
|
5765
5765
|
"version": "1.0.0",
|
|
5766
5766
|
"source": "file:vendor/skills/ios-enterprise-rules/SKILL.md",
|
|
5767
|
-
"hash": "
|
|
5767
|
+
"hash": "ad016db5ad38077a418d0bf7c3780b5ae8ff2bfc724691038a65292acc500787",
|
|
5768
5768
|
"rules": [
|
|
5769
5769
|
{
|
|
5770
5770
|
"id": "skills.ios.guideline.ios.accessibility-identifiers-para-localizar-elementos",
|
|
@@ -6727,18 +6727,6 @@
|
|
|
6727
6727
|
"evaluationMode": "DECLARATIVE",
|
|
6728
6728
|
"origin": "core"
|
|
6729
6729
|
},
|
|
6730
|
-
{
|
|
6731
|
-
"id": "skills.ios.guideline.ios.implementaciones-inyectadas-en-initializer-no-singleton",
|
|
6732
|
-
"description": "Implementaciones inyectadas - En initializer, no Singleton",
|
|
6733
|
-
"severity": "WARN",
|
|
6734
|
-
"platform": "ios",
|
|
6735
|
-
"sourceSkill": "ios-guidelines",
|
|
6736
|
-
"sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
|
|
6737
|
-
"confidence": "MEDIUM",
|
|
6738
|
-
"locked": true,
|
|
6739
|
-
"evaluationMode": "DECLARATIVE",
|
|
6740
|
-
"origin": "core"
|
|
6741
|
-
},
|
|
6742
6730
|
{
|
|
6743
6731
|
"id": "skills.ios.guideline.ios.implicitly-unwrapped-solo-para-iboutlets-y-casos-muy-especi-ficos",
|
|
6744
6732
|
"description": "Implicitly unwrapped (!) - Solo para IBOutlets y casos muy específicos",
|
|
@@ -6969,14 +6957,14 @@
|
|
|
6969
6957
|
},
|
|
6970
6958
|
{
|
|
6971
6959
|
"id": "skills.ios.guideline.ios.massive-view-controllers-viewcontrollers-que-mezclan-presentacio-n-nav",
|
|
6972
|
-
"description": "
|
|
6960
|
+
"description": "MVC (evitar) - Massive View Controller, no escalable",
|
|
6973
6961
|
"severity": "ERROR",
|
|
6974
6962
|
"platform": "ios",
|
|
6975
6963
|
"sourceSkill": "ios-guidelines",
|
|
6976
6964
|
"sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
|
|
6977
|
-
"confidence": "
|
|
6965
|
+
"confidence": "MEDIUM",
|
|
6978
6966
|
"locked": true,
|
|
6979
|
-
"evaluationMode": "
|
|
6967
|
+
"evaluationMode": "AUTO",
|
|
6980
6968
|
"origin": "core"
|
|
6981
6969
|
},
|
|
6982
6970
|
{
|
|
@@ -7003,18 +6991,6 @@
|
|
|
7003
6991
|
"evaluationMode": "DECLARATIVE",
|
|
7004
6992
|
"origin": "core"
|
|
7005
6993
|
},
|
|
7006
|
-
{
|
|
7007
|
-
"id": "skills.ios.guideline.ios.mvc-evitar-massive-view-controller-no-escalable",
|
|
7008
|
-
"description": "MVC (evitar) - Massive View Controller, no escalable",
|
|
7009
|
-
"severity": "ERROR",
|
|
7010
|
-
"platform": "ios",
|
|
7011
|
-
"sourceSkill": "ios-guidelines",
|
|
7012
|
-
"sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
|
|
7013
|
-
"confidence": "MEDIUM",
|
|
7014
|
-
"locked": true,
|
|
7015
|
-
"evaluationMode": "DECLARATIVE",
|
|
7016
|
-
"origin": "core"
|
|
7017
|
-
},
|
|
7018
6994
|
{
|
|
7019
6995
|
"id": "skills.ios.guideline.ios.mvvm-c-coordinator-para-navegacio-n",
|
|
7020
6996
|
"description": "MVVM-C - + Coordinator para navegación",
|
|
@@ -7180,19 +7156,7 @@
|
|
|
7180
7156
|
"sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
|
|
7181
7157
|
"confidence": "MEDIUM",
|
|
7182
7158
|
"locked": true,
|
|
7183
|
-
"evaluationMode": "
|
|
7184
|
-
"origin": "core"
|
|
7185
|
-
},
|
|
7186
|
-
{
|
|
7187
|
-
"id": "skills.ios.guideline.ios.no-singletons-excepto-sistema-urlsession-shared-esta-ok",
|
|
7188
|
-
"description": "No singletons - Excepto sistema (URLSession.shared está OK)",
|
|
7189
|
-
"severity": "WARN",
|
|
7190
|
-
"platform": "ios",
|
|
7191
|
-
"sourceSkill": "ios-guidelines",
|
|
7192
|
-
"sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
|
|
7193
|
-
"confidence": "MEDIUM",
|
|
7194
|
-
"locked": true,
|
|
7195
|
-
"evaluationMode": "DECLARATIVE",
|
|
7159
|
+
"evaluationMode": "AUTO",
|
|
7196
7160
|
"origin": "core"
|
|
7197
7161
|
},
|
|
7198
7162
|
{
|