pumuki 6.3.189 → 6.3.191
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/VERSION +1 -1
- package/core/facts/detectors/text/android.test.ts +37 -0
- package/core/facts/detectors/text/android.ts +7 -0
- package/core/facts/detectors/text/ios.test.ts +26 -0
- package/core/facts/detectors/text/ios.ts +27 -0
- package/core/facts/extractHeuristicFacts.ts +3 -0
- package/core/rules/presets/heuristics/android.test.ts +6 -1
- package/core/rules/presets/heuristics/android.ts +20 -0
- package/core/rules/presets/heuristics/ios.test.ts +11 -1
- package/core/rules/presets/heuristics/ios.ts +36 -0
- package/docs/codex-skills/android-enterprise-rules.md +3 -0
- package/docs/codex-skills/ios-enterprise-rules.md +5 -0
- package/integrations/config/skillsDetectorRegistry.ts +12 -0
- package/package.json +1 -1
- package/skills.lock.json +1 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v6.3.
|
|
1
|
+
v6.3.190
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
hasKotlinJUnit4Usage,
|
|
14
14
|
hasKotlinLiveDataStateExposureUsage,
|
|
15
15
|
hasKotlinLifecycleScopeUsage,
|
|
16
|
+
hasKotlinProductionMockUsage,
|
|
16
17
|
hasKotlinSharedPreferencesUsage,
|
|
17
18
|
hasKotlinWithContextUsage,
|
|
18
19
|
hasKotlinManualCoroutineScopeInViewModelUsage,
|
|
@@ -315,6 +316,42 @@ class OrdersViewModelTest {
|
|
|
315
316
|
assert.equal(hasKotlinJUnit4Usage(source), false);
|
|
316
317
|
});
|
|
317
318
|
|
|
319
|
+
test('hasKotlinProductionMockUsage detecta mocks y spies en Kotlin Android production', () => {
|
|
320
|
+
const mockkSource = `
|
|
321
|
+
class OrdersRepositoryFactory {
|
|
322
|
+
fun create(): OrdersRepository = mockk(relaxed = true)
|
|
323
|
+
}
|
|
324
|
+
`;
|
|
325
|
+
const mockitoSource = `
|
|
326
|
+
class OrdersRepositoryFactory {
|
|
327
|
+
private val api = Mockito.mock(OrdersApi::class.java)
|
|
328
|
+
}
|
|
329
|
+
`;
|
|
330
|
+
const annotationSource = `
|
|
331
|
+
class OrdersRepositoryFactory {
|
|
332
|
+
@Spy
|
|
333
|
+
lateinit var repository: OrdersRepository
|
|
334
|
+
}
|
|
335
|
+
`;
|
|
336
|
+
|
|
337
|
+
assert.equal(hasKotlinProductionMockUsage(mockkSource), true);
|
|
338
|
+
assert.equal(hasKotlinProductionMockUsage(mockitoSource), true);
|
|
339
|
+
assert.equal(hasKotlinProductionMockUsage(annotationSource), true);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
test('hasKotlinProductionMockUsage ignora imports, comentarios, strings y nombres parciales', () => {
|
|
343
|
+
const source = `
|
|
344
|
+
import io.mockk.mockk
|
|
345
|
+
import org.mockito.Mockito
|
|
346
|
+
// val api = mockk<OrdersApi>()
|
|
347
|
+
val sample = "Mockito.mock(OrdersApi::class.java)"
|
|
348
|
+
class OrdersRepositoryFactory {
|
|
349
|
+
fun create() = mockkitoFactory()
|
|
350
|
+
}
|
|
351
|
+
`;
|
|
352
|
+
assert.equal(hasKotlinProductionMockUsage(source), false);
|
|
353
|
+
});
|
|
354
|
+
|
|
318
355
|
test('hasKotlinSupervisorScopeUsage detecta supervisorScope con parentesis y llaves', () => {
|
|
319
356
|
const parenthesesSource = `
|
|
320
357
|
class SyncOrdersUseCase {
|
|
@@ -282,6 +282,13 @@ export const hasKotlinJUnit4Usage = (source: string): boolean => {
|
|
|
282
282
|
});
|
|
283
283
|
};
|
|
284
284
|
|
|
285
|
+
export const hasKotlinProductionMockUsage = (source: string): boolean => {
|
|
286
|
+
return collectKotlinRegexLines(
|
|
287
|
+
source,
|
|
288
|
+
/(?:\b(?:mockk|spyk|mockkObject|mockkStatic|mockkConstructor|mockito|mockitoSession|mockitoRule)\s*(?:<[^>\n]+>\s*)?\(|\bMockito\s*\.\s*(?:mock|spy)\s*\(|@(?:Mock|Spy|MockK|RelaxedMockK)\b)/
|
|
289
|
+
).length > 0;
|
|
290
|
+
};
|
|
291
|
+
|
|
285
292
|
export const hasKotlinSupervisorScopeUsage = (source: string): boolean => {
|
|
286
293
|
return collectKotlinRegexLines(source, /\bsupervisorScope\s*(?:<[^>\n]+>\s*)?(?:\(|\{)/).length > 0;
|
|
287
294
|
};
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
hasSwiftDispatchGroupUsage,
|
|
13
13
|
hasSwiftDispatchQueueUsage,
|
|
14
14
|
hasSwiftDispatchSemaphoreUsage,
|
|
15
|
+
hasSwiftAdHocLoggingUsage,
|
|
15
16
|
hasSwiftForEachIndicesUsage,
|
|
16
17
|
hasSwiftForceCastUsage,
|
|
17
18
|
hasSwiftFontWeightBoldUsage,
|
|
@@ -41,6 +42,7 @@ import {
|
|
|
41
42
|
hasSwiftPreconcurrencyUsage,
|
|
42
43
|
hasSwiftSheetIsPresentedUsage,
|
|
43
44
|
hasSwiftScrollViewShowsIndicatorsUsage,
|
|
45
|
+
hasSwiftSensitiveLoggingUsage,
|
|
44
46
|
hasSwiftStringFormatUsage,
|
|
45
47
|
hasSwiftTabItemUsage,
|
|
46
48
|
hasSwiftTaskDetachedUsage,
|
|
@@ -165,6 +167,30 @@ Task {
|
|
|
165
167
|
assert.equal(hasSwiftTaskDetachedUsage(negative), false);
|
|
166
168
|
});
|
|
167
169
|
|
|
170
|
+
test('detectores de logging iOS detectan logs ad-hoc y PII en produccion', () => {
|
|
171
|
+
const adHoc = `
|
|
172
|
+
print(user.id)
|
|
173
|
+
debugPrint(response)
|
|
174
|
+
dump(model)
|
|
175
|
+
NSLog("legacy")
|
|
176
|
+
os_log("legacy")
|
|
177
|
+
`;
|
|
178
|
+
const structuredSafe = `
|
|
179
|
+
logger.info("Screen loaded")
|
|
180
|
+
let text = "print(accessToken)"
|
|
181
|
+
// print(accessToken)
|
|
182
|
+
`;
|
|
183
|
+
const sensitive = `
|
|
184
|
+
print(accessToken)
|
|
185
|
+
logger.error("Refresh failed \\(refreshToken)")
|
|
186
|
+
`;
|
|
187
|
+
|
|
188
|
+
assert.equal(hasSwiftAdHocLoggingUsage(adHoc), true);
|
|
189
|
+
assert.equal(hasSwiftAdHocLoggingUsage(structuredSafe), false);
|
|
190
|
+
assert.equal(hasSwiftSensitiveLoggingUsage(sensitive), true);
|
|
191
|
+
assert.equal(hasSwiftSensitiveLoggingUsage(structuredSafe), false);
|
|
192
|
+
});
|
|
193
|
+
|
|
168
194
|
test('hasSwiftUncheckedSendableUsage detecta @unchecked Sendable', () => {
|
|
169
195
|
const source = `
|
|
170
196
|
final class LegacyBox: @unchecked Sendable {}
|
|
@@ -437,6 +437,33 @@ export const hasSwiftTaskDetachedUsage = (source: string): boolean => {
|
|
|
437
437
|
});
|
|
438
438
|
};
|
|
439
439
|
|
|
440
|
+
export const hasSwiftAdHocLoggingUsage = (source: string): boolean => {
|
|
441
|
+
return collectSwiftRegexLines(
|
|
442
|
+
source,
|
|
443
|
+
/\b(?:print|debugPrint|dump|NSLog|os_log)\s*\(/
|
|
444
|
+
).length > 0;
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
export const hasSwiftSensitiveLoggingUsage = (source: string): boolean => {
|
|
448
|
+
return source.split(/\r?\n/).some((line) => {
|
|
449
|
+
const sanitized = stripSwiftLineForSemanticScan(line);
|
|
450
|
+
const lineWithoutComments = line.replace(/\/\/.*$/, '');
|
|
451
|
+
const hasLoggingCall =
|
|
452
|
+
/\b(?:print|debugPrint|dump|NSLog|os_log)\s*\(/.test(sanitized) ||
|
|
453
|
+
/\b(?:logger|log)\s*\.\s*(?:debug|info|notice|warning|error|critical|log)\s*\(/i.test(
|
|
454
|
+
sanitized
|
|
455
|
+
);
|
|
456
|
+
|
|
457
|
+
if (!hasLoggingCall) {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return /\b(?:accessToken|refreshToken|authToken|token|password|secret|credential|authorization|email|userId)\b/i.test(
|
|
462
|
+
lineWithoutComments
|
|
463
|
+
);
|
|
464
|
+
});
|
|
465
|
+
};
|
|
466
|
+
|
|
440
467
|
export const hasSwiftUncheckedSendableUsage = (source: string): boolean => {
|
|
441
468
|
return scanCodeLikeSource(source, ({ source: swiftSource, index, current }) => {
|
|
442
469
|
if (current !== '@' || !swiftSource.startsWith('@unchecked', index)) {
|
|
@@ -618,6 +618,8 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
|
|
|
618
618
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftDispatchSemaphoreUsage, ruleId: 'heuristics.ios.dispatchsemaphore.ast', code: 'HEURISTICS_IOS_DISPATCHSEMAPHORE_AST', message: 'AST heuristic detected DispatchSemaphore usage.' },
|
|
619
619
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftOperationQueueUsage, ruleId: 'heuristics.ios.operation-queue.ast', code: 'HEURISTICS_IOS_OPERATION_QUEUE_AST', message: 'AST heuristic detected OperationQueue usage.' },
|
|
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
|
+
{ 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
|
+
{ 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.' },
|
|
621
623
|
{ 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.' },
|
|
622
624
|
{ 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.' },
|
|
623
625
|
{ 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.' },
|
|
@@ -658,6 +660,7 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
|
|
|
658
660
|
{ platform: 'android', pathCheck: isAndroidKotlinPath, excludePaths: [isKotlinTestPath], detect: TextAndroid.hasKotlinRunBlockingUsage, ruleId: 'heuristics.android.run-blocking.ast', code: 'HEURISTICS_ANDROID_RUN_BLOCKING_AST', message: 'AST heuristic detected runBlocking usage in production Kotlin code.' },
|
|
659
661
|
{ platform: 'android', pathCheck: isAndroidLocalPropertiesPath, excludePaths: [], detect: detectsTrackedFilePresence, ruleId: 'heuristics.android.security.local-properties-tracked.ast', code: 'HEURISTICS_ANDROID_SECURITY_LOCAL_PROPERTIES_TRACKED_AST', message: 'AST heuristic detected tracked Android local.properties file.' },
|
|
660
662
|
{ platform: 'android', pathCheck: isAndroidKotlinPath, excludePaths: [isKotlinTestPath], detect: TextAndroid.hasKotlinSharedPreferencesUsage, ruleId: 'heuristics.android.persistence.shared-preferences-usage.ast', code: 'HEURISTICS_ANDROID_PERSISTENCE_SHARED_PREFERENCES_USAGE_AST', message: 'AST heuristic detected SharedPreferences usage in Android production Kotlin code.' },
|
|
663
|
+
{ platform: 'android', pathCheck: isAndroidKotlinPath, excludePaths: [isKotlinTestPath], detect: TextAndroid.hasKotlinProductionMockUsage, ruleId: 'heuristics.android.testing.production-mock-usage.ast', code: 'HEURISTICS_ANDROID_TESTING_PRODUCTION_MOCK_USAGE_AST', message: 'AST heuristic detected mock or spy usage in Android production Kotlin code.' },
|
|
661
664
|
{ platform: 'android', pathCheck: isAndroidKotlinTestPath, excludePaths: [], detect: TextAndroid.hasKotlinJUnit4Usage, ruleId: 'heuristics.android.testing.junit4-usage.ast', code: 'HEURISTICS_ANDROID_TESTING_JUNIT4_USAGE_AST', message: 'AST heuristic detected JUnit4 usage in Android Kotlin tests where JUnit5 is preferred.' },
|
|
662
665
|
{ platform: 'android', pathCheck: isAndroidPresentationPath, excludePaths: [isKotlinTestPath], detect: TextAndroid.hasKotlinLiveDataStateExposureUsage, ruleId: 'heuristics.android.flow.livedata-state-exposure.ast', code: 'HEURISTICS_ANDROID_FLOW_LIVEDATA_STATE_EXPOSURE_AST', message: 'AST heuristic detected LiveData state exposure in Android presentation code where StateFlow or SharedFlow should be preferred.' },
|
|
663
666
|
{ platform: 'android', pathCheck: isAndroidPresentationPath, excludePaths: [isKotlinTestPath], detect: TextAndroid.hasKotlinManualCoroutineScopeInViewModelUsage, ruleId: 'heuristics.android.coroutines.manual-scope-in-viewmodel.ast', code: 'HEURISTICS_ANDROID_COROUTINES_MANUAL_SCOPE_IN_VIEWMODEL_AST', message: 'AST heuristic detected manual CoroutineScope inside an Android ViewModel where viewModelScope should be preferred.' },
|
|
@@ -3,7 +3,7 @@ import test from 'node:test';
|
|
|
3
3
|
import { androidRules } from './android';
|
|
4
4
|
|
|
5
5
|
test('androidRules define reglas heurísticas locked para plataforma android', () => {
|
|
6
|
-
assert.equal(androidRules.length,
|
|
6
|
+
assert.equal(androidRules.length, 20);
|
|
7
7
|
|
|
8
8
|
const ids = androidRules.map((rule) => rule.id);
|
|
9
9
|
assert.deepEqual(ids, [
|
|
@@ -14,6 +14,7 @@ test('androidRules define reglas heurísticas locked para plataforma android', (
|
|
|
14
14
|
'heuristics.android.security.local-properties-tracked.ast',
|
|
15
15
|
'heuristics.android.persistence.shared-preferences-usage.ast',
|
|
16
16
|
'heuristics.android.testing.junit4-usage.ast',
|
|
17
|
+
'heuristics.android.testing.production-mock-usage.ast',
|
|
17
18
|
'heuristics.android.coroutines.manual-scope-in-viewmodel.ast',
|
|
18
19
|
'heuristics.android.coroutines.dispatchers-main-boundary-leak.ast',
|
|
19
20
|
'heuristics.android.coroutines.hardcoded-background-dispatcher.ast',
|
|
@@ -67,6 +68,10 @@ test('androidRules define reglas heurísticas locked para plataforma android', (
|
|
|
67
68
|
byId.get('heuristics.android.testing.junit4-usage.ast')?.then.code,
|
|
68
69
|
'HEURISTICS_ANDROID_TESTING_JUNIT4_USAGE_AST'
|
|
69
70
|
);
|
|
71
|
+
assert.equal(
|
|
72
|
+
byId.get('heuristics.android.testing.production-mock-usage.ast')?.then.code,
|
|
73
|
+
'HEURISTICS_ANDROID_TESTING_PRODUCTION_MOCK_USAGE_AST'
|
|
74
|
+
);
|
|
70
75
|
assert.equal(
|
|
71
76
|
byId.get('heuristics.android.coroutines.manual-scope-in-viewmodel.ast')?.then.code,
|
|
72
77
|
'HEURISTICS_ANDROID_COROUTINES_MANUAL_SCOPE_IN_VIEWMODEL_AST'
|
|
@@ -135,6 +135,26 @@ export const androidRules: RuleSet = [
|
|
|
135
135
|
code: 'HEURISTICS_ANDROID_TESTING_JUNIT4_USAGE_AST',
|
|
136
136
|
},
|
|
137
137
|
},
|
|
138
|
+
{
|
|
139
|
+
id: 'heuristics.android.testing.production-mock-usage.ast',
|
|
140
|
+
description:
|
|
141
|
+
'Detects mock or spy usage in Android production Kotlin code.',
|
|
142
|
+
severity: 'WARN',
|
|
143
|
+
platform: 'android',
|
|
144
|
+
locked: true,
|
|
145
|
+
when: {
|
|
146
|
+
kind: 'Heuristic',
|
|
147
|
+
where: {
|
|
148
|
+
ruleId: 'heuristics.android.testing.production-mock-usage.ast',
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
then: {
|
|
152
|
+
kind: 'Finding',
|
|
153
|
+
message:
|
|
154
|
+
'AST heuristic detected mock or spy usage in Android production Kotlin code.',
|
|
155
|
+
code: 'HEURISTICS_ANDROID_TESTING_PRODUCTION_MOCK_USAGE_AST',
|
|
156
|
+
},
|
|
157
|
+
},
|
|
138
158
|
{
|
|
139
159
|
id: 'heuristics.android.coroutines.manual-scope-in-viewmodel.ast',
|
|
140
160
|
description:
|
|
@@ -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, 45);
|
|
7
7
|
|
|
8
8
|
const ids = iosRules.map((rule) => rule.id);
|
|
9
9
|
assert.deepEqual(ids, [
|
|
@@ -17,6 +17,8 @@ test('iosRules define reglas heurísticas locked para plataforma ios', () => {
|
|
|
17
17
|
'heuristics.ios.dispatchsemaphore.ast',
|
|
18
18
|
'heuristics.ios.operation-queue.ast',
|
|
19
19
|
'heuristics.ios.task-detached.ast',
|
|
20
|
+
'heuristics.ios.logging.adhoc-print.ast',
|
|
21
|
+
'heuristics.ios.logging.sensitive-data.ast',
|
|
20
22
|
'heuristics.ios.unchecked-sendable.ast',
|
|
21
23
|
'heuristics.ios.preconcurrency.ast',
|
|
22
24
|
'heuristics.ios.nonisolated-unsafe.ast',
|
|
@@ -61,6 +63,14 @@ test('iosRules define reglas heurísticas locked para plataforma ios', () => {
|
|
|
61
63
|
byId.get('heuristics.ios.task-detached.ast')?.then.code,
|
|
62
64
|
'HEURISTICS_IOS_TASK_DETACHED_AST'
|
|
63
65
|
);
|
|
66
|
+
assert.equal(
|
|
67
|
+
byId.get('heuristics.ios.logging.adhoc-print.ast')?.then.code,
|
|
68
|
+
'HEURISTICS_IOS_LOGGING_ADHOC_PRINT_AST'
|
|
69
|
+
);
|
|
70
|
+
assert.equal(
|
|
71
|
+
byId.get('heuristics.ios.logging.sensitive-data.ast')?.then.code,
|
|
72
|
+
'HEURISTICS_IOS_LOGGING_SENSITIVE_DATA_AST'
|
|
73
|
+
);
|
|
64
74
|
assert.equal(
|
|
65
75
|
byId.get('heuristics.ios.preconcurrency.ast')?.then.code,
|
|
66
76
|
'HEURISTICS_IOS_PRECONCURRENCY_AST'
|
|
@@ -181,6 +181,42 @@ export const iosRules: RuleSet = [
|
|
|
181
181
|
code: 'HEURISTICS_IOS_TASK_DETACHED_AST',
|
|
182
182
|
},
|
|
183
183
|
},
|
|
184
|
+
{
|
|
185
|
+
id: 'heuristics.ios.logging.adhoc-print.ast',
|
|
186
|
+
description: 'Detects print/debugPrint/dump/NSLog/os_log usage in iOS production code.',
|
|
187
|
+
severity: 'WARN',
|
|
188
|
+
platform: 'ios',
|
|
189
|
+
locked: true,
|
|
190
|
+
when: {
|
|
191
|
+
kind: 'Heuristic',
|
|
192
|
+
where: {
|
|
193
|
+
ruleId: 'heuristics.ios.logging.adhoc-print.ast',
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
then: {
|
|
197
|
+
kind: 'Finding',
|
|
198
|
+
message: 'AST heuristic detected print/debugPrint/dump/NSLog/os_log usage in iOS production code.',
|
|
199
|
+
code: 'HEURISTICS_IOS_LOGGING_ADHOC_PRINT_AST',
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
id: 'heuristics.ios.logging.sensitive-data.ast',
|
|
204
|
+
description: 'Detects sensitive data in iOS logging calls.',
|
|
205
|
+
severity: 'WARN',
|
|
206
|
+
platform: 'ios',
|
|
207
|
+
locked: true,
|
|
208
|
+
when: {
|
|
209
|
+
kind: 'Heuristic',
|
|
210
|
+
where: {
|
|
211
|
+
ruleId: 'heuristics.ios.logging.sensitive-data.ast',
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
then: {
|
|
215
|
+
kind: 'Finding',
|
|
216
|
+
message: 'AST heuristic detected sensitive data in an iOS logging call.',
|
|
217
|
+
code: 'HEURISTICS_IOS_LOGGING_SENSITIVE_DATA_AST',
|
|
218
|
+
},
|
|
219
|
+
},
|
|
184
220
|
{
|
|
185
221
|
id: 'heuristics.ios.unchecked-sendable.ast',
|
|
186
222
|
description: 'Detects @unchecked Sendable usage in iOS production code.',
|
|
@@ -150,6 +150,9 @@ app/
|
|
|
150
150
|
✅ `skills.android.guideline.android.junit5-framework-de-testing-preferido-sobre-junit4` debe mapear a señal ejecutable de uso JUnit4 en tests Kotlin Android.
|
|
151
151
|
✅ Este baseline detecta imports/anotaciones JUnit4 (`org.junit.Test`, `org.junit.Assert`, `@RunWith`, reglas JUnit4) bajo `apps/android/**/test/**` y `apps/android/**/androidTest/**`.
|
|
152
152
|
✅ JUnit5/Jupiter queda preservado como caso limpio; migración completa de runner, Gradle y APIs de assertions queda fuera hasta tener detectores propios.
|
|
153
|
+
✅ `skills.android.guideline.android.en-produccio-n-ni-un-mocks-ni-un-spies-todo-real-de-apis-y-persistenci` debe mapear a señal ejecutable de mocks/spies en Kotlin Android production.
|
|
154
|
+
✅ Este baseline detecta usos explícitos de MockK/Mockito y anotaciones `@Mock`/`@Spy` fuera de `test` y `androidTest`; los dobles en tests siguen permitidos.
|
|
155
|
+
✅ La regla no prohíbe clases fake de test doubles en suites de test ni analiza wiring Gradle todavía.
|
|
153
156
|
|
|
154
157
|
### Dependency Injection (Hilt):
|
|
155
158
|
✅ **Hilt** - DI framework (NO manual factories)
|
|
@@ -223,6 +223,11 @@ apps/ios/Presentation/
|
|
|
223
223
|
✅ **Prohibido print()** y logs ad-hoc
|
|
224
224
|
✅ **No loggear PII** (tokens, emails, IDs sensibles)
|
|
225
225
|
|
|
226
|
+
### Enforcement AST inicial de logging iOS:
|
|
227
|
+
✅ `skills.ios.guideline.ios.prohibido-print-y-logs-ad-hoc` se mapea a `heuristics.ios.logging.adhoc-print.ast` para detectar `print`, `debugPrint`, `dump`, `NSLog` y `os_log` en Swift production.
|
|
228
|
+
✅ `skills.ios.guideline.ios.no-loggear-pii-tokens-emails-ids-sensibles` se mapea a `heuristics.ios.logging.sensitive-data.ast` para detectar tokens, credenciales, emails e IDs sensibles en llamadas de logging.
|
|
229
|
+
✅ `os.Logger` sigue siendo la API preferida; esta slice detecta el riesgo prohibido, no fuerza todavía una arquitectura completa de observabilidad.
|
|
230
|
+
|
|
226
231
|
```swift
|
|
227
232
|
// ✅ Ejemplo: ViewModel con @Observable (iOS 17+)
|
|
228
233
|
@Observable
|
|
@@ -47,6 +47,14 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
|
|
|
47
47
|
'skills.ios.no-task-detached': heuristicDetector('ios.task-detached', [
|
|
48
48
|
'heuristics.ios.task-detached.ast',
|
|
49
49
|
]),
|
|
50
|
+
'skills.ios.guideline.ios.prohibido-print-y-logs-ad-hoc': heuristicDetector(
|
|
51
|
+
'ios.logging.adhoc-print',
|
|
52
|
+
['heuristics.ios.logging.adhoc-print.ast']
|
|
53
|
+
),
|
|
54
|
+
'skills.ios.guideline.ios.no-loggear-pii-tokens-emails-ids-sensibles': heuristicDetector(
|
|
55
|
+
'ios.logging.sensitive-data',
|
|
56
|
+
['heuristics.ios.logging.sensitive-data.ast']
|
|
57
|
+
),
|
|
50
58
|
'skills.ios.no-unchecked-sendable': heuristicDetector('ios.unchecked-sendable', [
|
|
51
59
|
'heuristics.ios.unchecked-sendable.ast',
|
|
52
60
|
]),
|
|
@@ -259,6 +267,10 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
|
|
|
259
267
|
'android.testing.junit4-usage',
|
|
260
268
|
['heuristics.android.testing.junit4-usage.ast']
|
|
261
269
|
),
|
|
270
|
+
'skills.android.guideline.android.en-produccio-n-ni-un-mocks-ni-un-spies-todo-real-de-apis-y-persistenci': heuristicDetector(
|
|
271
|
+
'android.testing.production-mock-usage',
|
|
272
|
+
['heuristics.android.testing.production-mock-usage.ast']
|
|
273
|
+
),
|
|
262
274
|
'skills.android.guideline.android.stateflow-estado-mutable-observable': heuristicDetector(
|
|
263
275
|
'android.flow.state-exposure',
|
|
264
276
|
['heuristics.android.flow.livedata-state-exposure.ast']
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.191",
|
|
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": {
|