@prasenjeet/shipli 1.2.0 → 1.3.0

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/README.md CHANGED
@@ -3,6 +3,8 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@prasenjeet/shipli)](https://www.npmjs.com/package/@prasenjeet/shipli)
4
4
  [![license](https://img.shields.io/npm/l/@prasenjeet/shipli)](LICENSE)
5
5
 
6
+ **Website:** [https://prasenjeet-symon.github.io/shipli-ai/](https://prasenjeet-symon.github.io/shipli-ai/)
7
+
6
8
  Store rejections cost days of development time. **Shipli** is a CLI tool that audits your Flutter source code against **Apple App Store** and **Google Play** guidelines using AI.
7
9
 
8
10
  Catch missing permissions, policy violations, and compliance issues before you submit.
@@ -233,6 +235,24 @@ The assistant will call the appropriate Shipli MCP tool and return the results i
233
235
 
234
236
  The CLI exits with code `1` on failure, making it easy to gate deployments.
235
237
 
238
+ ## Telemetry
239
+
240
+ Shipli collects anonymous usage metrics to help improve the tool. No project data, file contents, API keys, or personally identifiable information is ever collected.
241
+
242
+ **What's collected:** audit mode, platform, provider, model, pass/fail score, duration, token counts, OS, and CLI version.
243
+
244
+ **Opt out** using any of these methods:
245
+
246
+ ```bash
247
+ # Environment variable
248
+ export SHIPLI_TELEMETRY=off
249
+
250
+ # Or use the Do Not Track standard
251
+ export DO_NOT_TRACK=1
252
+ ```
253
+
254
+ Or add `"telemetry": false` to your `~/.shipli` config file.
255
+
236
256
  ## License
237
257
 
238
258
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prasenjeet/shipli",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "AI-powered App Store review audit for Flutter projects. Catch rejections before you submit.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -28,9 +28,10 @@
28
28
  ],
29
29
  "author": "",
30
30
  "license": "MIT",
31
+ "homepage": "https://prasenjeet-symon.github.io/shipli-ai/",
31
32
  "repository": {
32
33
  "type": "git",
33
- "url": ""
34
+ "url": "https://github.com/prasenjeet-symon/shipli-ai"
34
35
  },
35
36
  "dependencies": {
36
37
  "@modelcontextprotocol/sdk": "^1.27.1",
package/src/auditor.js CHANGED
@@ -129,11 +129,14 @@ function buildCodePrompt() {
129
129
  - Performance optimization for Flutter (build methods, rebuilds, isolates)
130
130
  - Dependency management and pub.dev ecosystem health
131
131
 
132
+ You have been provided a "FLUTTER_CODE_RULES" section containing a security and code quality rulebook. Use these rules as a baseline checklist: ensure every relevant clause is evaluated against the project evidence. However, you are NOT limited to these rules — also apply your own expertise to identify issues beyond the rulebook.
133
+
132
134
  Focus ONLY on code quality, security, and engineering best practices — not store compliance or legal requirements.
133
135
 
134
136
  EVIDENCE FORMAT:
135
137
  - "PUBSPEC_METADATA": App name, version, and list of pub.dev package dependencies
136
138
  - "DART_SKELETONS": Architectural skeleton of Dart files
139
+ - "FLUTTER_CODE_RULES": Security and code quality rulebook with specific vulnerability patterns and mitigations (if available)
137
140
  ${CODE_CATEGORIES}
138
141
  ${RESPONSE_FORMAT}`;
139
142
  }
@@ -184,6 +187,8 @@ function buildBothPrompt(platform) {
184
187
 
185
188
  const platformLabel = isBoth ? 'Apple App Store and Google Play' : isIos ? 'Apple App Store' : 'Google Play Store';
186
189
 
190
+ evidence.push('- "FLUTTER_CODE_RULES": Security and code quality rulebook with specific vulnerability patterns and mitigations (if available)');
191
+
187
192
  return `You are an experienced ${platformLabel} reviewer AND senior Flutter/Dart engineer conducting a comprehensive audit.
188
193
 
189
194
  ${guidelinesRef.join('\n')}
@@ -191,6 +196,8 @@ ${guidelinesRef.join('\n')}
191
196
  You have deep knowledge of:
192
197
  ${knowledge.join('\n')}
193
198
 
199
+ You have been provided a "FLUTTER_CODE_RULES" section containing a security and code quality rulebook. Use these rules as a baseline checklist: ensure every relevant clause is evaluated against the project evidence. However, you are NOT limited to these rules — also apply your own expertise to identify issues beyond the rulebook.
200
+
194
201
  Analyze the provided Flutter project evidence and produce a comprehensive audit report covering both store compliance and code quality.
195
202
 
196
203
  EVIDENCE FORMAT:
@@ -243,6 +250,8 @@ function buildPkgCodePrompt() {
243
250
  - Flutter plugin architecture and platform channel patterns
244
251
  - Performance and memory management in plugins
245
252
 
253
+ You have been provided a "FLUTTER_CODE_RULES" section containing a security and code quality rulebook. Use these rules as a baseline checklist: ensure every relevant clause is evaluated against the project evidence. Not all rules may apply to a standalone package — skip clauses that are not relevant. However, you are NOT limited to these rules — also apply your own expertise to identify issues beyond the rulebook.
254
+
246
255
  Focus ONLY on code quality, API design, and engineering best practices — not store compliance.
247
256
 
248
257
  EVIDENCE FORMAT:
@@ -250,6 +259,7 @@ EVIDENCE FORMAT:
250
259
  - "PLUGIN_PLATFORMS": Supported platforms and native integration classes
251
260
  - "DART_SKELETONS": Architectural skeleton of Dart files
252
261
  - "EXAMPLE_APP" (if present): Skeleton of the example/ app
262
+ - "FLUTTER_CODE_RULES": Security and code quality rulebook with specific vulnerability patterns and mitigations (if available)
253
263
  ${PKG_CODE_CATEGORIES}
254
264
  ${RESPONSE_FORMAT}`;
255
265
  }
@@ -263,6 +273,8 @@ function buildPkgBothPrompt() {
263
273
  - iOS and Android platform integration requirements
264
274
  - Privacy and permission implications for consuming apps
265
275
 
276
+ You have been provided a "FLUTTER_CODE_RULES" section containing a security and code quality rulebook. Use these rules as a baseline checklist: ensure every relevant clause is evaluated against the project evidence. Not all rules may apply to a standalone package — skip clauses that are not relevant. However, you are NOT limited to these rules — also apply your own expertise to identify issues beyond the rulebook.
277
+
266
278
  Analyze the provided Flutter package/plugin evidence and produce a comprehensive audit report.
267
279
 
268
280
  EVIDENCE FORMAT:
@@ -270,6 +282,7 @@ EVIDENCE FORMAT:
270
282
  - "PLUGIN_PLATFORMS": Supported platforms and native integration classes
271
283
  - "DART_SKELETONS": Architectural skeleton of Dart files
272
284
  - "EXAMPLE_APP" (if present): Skeleton of the example/ app
285
+ - "FLUTTER_CODE_RULES": Security and code quality rulebook with specific vulnerability patterns and mitigations (if available)
273
286
  ${PKG_STORE_CATEGORIES}
274
287
  ${PKG_CODE_CATEGORIES}
275
288
  ${RESPONSE_FORMAT}`;
@@ -291,7 +304,7 @@ function selectPrompt(projectType, mode, platform) {
291
304
 
292
305
  // ── Message builder ──
293
306
 
294
- function buildUserMessage({ files, exampleFiles, permissions, androidPermissions, pubspec, plistFound, androidManifestFound, projectType, appleGuidelines, googleGuidelines }) {
307
+ function buildUserMessage({ files, exampleFiles, permissions, androidPermissions, pubspec, plistFound, androidManifestFound, projectType, appleGuidelines, googleGuidelines, codeRules }) {
295
308
  const sections = [];
296
309
  const isPackage = projectType === 'package';
297
310
 
@@ -308,6 +321,12 @@ function buildUserMessage({ files, exampleFiles, permissions, androidPermissions
308
321
  sections.push(googleGuidelines.content);
309
322
  }
310
323
 
324
+ if (codeRules?.content) {
325
+ sections.push('\n=== FLUTTER_CODE_RULES ===');
326
+ sections.push('(Security and code quality rulebook — use as a baseline checklist)');
327
+ sections.push(codeRules.content);
328
+ }
329
+
311
330
  sections.push('\n=== PUBSPEC_METADATA ===');
312
331
  sections.push(`${isPackage ? 'Package' : 'App'}: ${pubspec.name}${pubspec.version ? ` v${pubspec.version}` : ''}`);
313
332
  if (pubspec.description) {
package/src/guidelines.js CHANGED
@@ -13,6 +13,10 @@ const STORES = {
13
13
  file: join(__dirname, 'rules', 'android-rules.md'),
14
14
  label: 'Play Store',
15
15
  },
16
+ flutter_code: {
17
+ file: join(__dirname, 'rules', 'flutter-code-rules.md'),
18
+ label: 'Flutter Code Rules',
19
+ },
16
20
  };
17
21
 
18
22
  /**
package/src/index.js CHANGED
@@ -18,6 +18,7 @@ import { audit } from './auditor.js';
18
18
  import { fetchGuidelines } from './guidelines.js';
19
19
  import { print as printReport } from './reporter.js';
20
20
  import { PROVIDER_DEFAULTS as DEFAULTS, detectPlatform } from './defaults.js';
21
+ import { trackEvent } from './telemetry.js';
21
22
 
22
23
  // Read package version
23
24
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -113,6 +114,7 @@ program
113
114
 
114
115
  const platformLabel = { ios: 'iOS', android: 'Android', both: 'iOS + Android' }[platform];
115
116
  let spinner = ora({ text: `Scanning Flutter project (${platformLabel})...`, color: 'cyan' }).start();
117
+ const startTime = Date.now();
116
118
 
117
119
  try {
118
120
  // 1. Read pubspec.yaml first (needed for type detection)
@@ -174,7 +176,18 @@ program
174
176
  }
175
177
  }
176
178
 
177
- // 7. Run AI audit
179
+ // 7. Load code review rules (for code and both modes)
180
+ let codeRules = null;
181
+ if (mode !== 'store') {
182
+ spinner.text = 'Loading code review rules...';
183
+ codeRules = await fetchGuidelines('flutter_code');
184
+ if (codeRules.warning) {
185
+ spinner.warn(chalk.yellow(codeRules.warning));
186
+ spinner = ora({ text: '', color: 'cyan' }).start();
187
+ }
188
+ }
189
+
190
+ // 8. Run AI audit
178
191
  const modeLabel = { store: 'store compliance', code: 'code quality', both: 'full' }[mode];
179
192
  spinner.text = `Running ${modeLabel} audit with ${provider}/${model}...`;
180
193
  const result = await audit(
@@ -189,6 +202,7 @@ program
189
202
  projectType,
190
203
  appleGuidelines,
191
204
  googleGuidelines,
205
+ codeRules,
192
206
  },
193
207
  { apiKey, model, provider, mode, platform },
194
208
  );
@@ -205,11 +219,23 @@ program
205
219
  platform,
206
220
  });
207
221
 
208
- // 9. Exit with appropriate code
209
- process.exit(result.score === 'FAIL' ? 1 : 0);
222
+ // 9. Track and exit
223
+ trackEvent('audit_completed', {
224
+ source: 'cli', mode, platform, provider, model,
225
+ project_type: projectType, score: result.score,
226
+ duration_ms: Date.now() - startTime,
227
+ tokens_input: result._tokens?.actual?.input ?? null,
228
+ tokens_output: result._tokens?.actual?.output ?? null,
229
+ });
230
+ process.exitCode = result.score === 'FAIL' ? 1 : 0;
210
231
  } catch (err) {
211
232
  spinner.fail(chalk.red(err.message));
212
- process.exit(1);
233
+ trackEvent('audit_error', {
234
+ source: 'cli', mode, platform, provider, model,
235
+ duration_ms: Date.now() - startTime,
236
+ error_message: err.message.slice(0, 200),
237
+ });
238
+ process.exitCode = 1;
213
239
  }
214
240
  });
215
241
 
package/src/mcp-server.js CHANGED
@@ -15,6 +15,7 @@ import { fetchGuidelines } from './guidelines.js';
15
15
  import { audit } from './auditor.js';
16
16
  import { loadConfig } from './config.js';
17
17
  import { PROVIDER_DEFAULTS, detectPlatform } from './defaults.js';
18
+ import { trackEvent } from './telemetry.js';
18
19
 
19
20
  // ── Read package version ──
20
21
 
@@ -72,6 +73,7 @@ server.tool(
72
73
  platform: z.enum(['ios', 'android', 'both']).optional().describe('Target platform: "ios" (App Store only), "android" (Play Store only), or "both" (both stores). Auto-detected from ios/ and android/ directory presence if omitted.'),
73
74
  },
74
75
  async ({ projectDir, platform }) => {
76
+ const startTime = Date.now();
75
77
  try {
76
78
  const dir = validateProject(projectDir);
77
79
  const { provider, model, apiKey } = resolveConfig(dir);
@@ -124,10 +126,23 @@ server.tool(
124
126
  { apiKey, model, provider, mode: 'store', platform: resolvedPlatform },
125
127
  );
126
128
 
129
+ trackEvent('audit_completed', {
130
+ source: 'mcp', mode: 'store', platform: resolvedPlatform,
131
+ provider, model, project_type: projectType, score: result.score,
132
+ duration_ms: Date.now() - startTime,
133
+ tokens_input: result._tokens?.actual?.input ?? null,
134
+ tokens_output: result._tokens?.actual?.output ?? null,
135
+ });
136
+
127
137
  return {
128
138
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
129
139
  };
130
140
  } catch (err) {
141
+ trackEvent('audit_error', {
142
+ source: 'mcp', mode: 'store',
143
+ duration_ms: Date.now() - startTime,
144
+ error_message: err.message.slice(0, 200),
145
+ });
131
146
  return {
132
147
  content: [{ type: 'text', text: `Error: ${err.message}` }],
133
148
  isError: true,
@@ -145,6 +160,7 @@ server.tool(
145
160
  projectDir: z.string().describe('Absolute path to the Flutter project root directory. Must contain a pubspec.yaml and a lib/ folder. Example: "/Users/you/projects/my-flutter-app"'),
146
161
  },
147
162
  async ({ projectDir }) => {
163
+ const startTime = Date.now();
148
164
  try {
149
165
  const dir = validateProject(projectDir);
150
166
  const { provider, model, apiKey } = resolveConfig(dir);
@@ -160,6 +176,8 @@ server.tool(
160
176
  exampleFiles = exampleResult.files;
161
177
  }
162
178
 
179
+ const codeRules = await fetchGuidelines('flutter_code');
180
+
163
181
  const result = await audit(
164
182
  {
165
183
  files,
@@ -172,14 +190,28 @@ server.tool(
172
190
  projectType,
173
191
  appleGuidelines: null,
174
192
  googleGuidelines: null,
193
+ codeRules,
175
194
  },
176
195
  { apiKey, model, provider, mode: 'code', platform: 'both' },
177
196
  );
178
197
 
198
+ trackEvent('audit_completed', {
199
+ source: 'mcp', mode: 'code', platform: 'both',
200
+ provider, model, project_type: projectType, score: result.score,
201
+ duration_ms: Date.now() - startTime,
202
+ tokens_input: result._tokens?.actual?.input ?? null,
203
+ tokens_output: result._tokens?.actual?.output ?? null,
204
+ });
205
+
179
206
  return {
180
207
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
181
208
  };
182
209
  } catch (err) {
210
+ trackEvent('audit_error', {
211
+ source: 'mcp', mode: 'code',
212
+ duration_ms: Date.now() - startTime,
213
+ error_message: err.message.slice(0, 200),
214
+ });
183
215
  return {
184
216
  content: [{ type: 'text', text: `Error: ${err.message}` }],
185
217
  isError: true,
@@ -0,0 +1,247 @@
1
+ ## Clause I: Security Anti-Patterns and Cryptographic Vulnerabilities
2
+
3
+ ### Sub-Clause 1.1: Insecure Data Persistence and Secret Management
4
+ - Developers routinely misuse **SharedPreferences** (and iOS **UserDefaults**) to store sensitive data such as OAuth tokens, session identifiers, credentials, and encryption keys.
5
+ - These libraries store data in plaintext XML/plist files, easily extractable if the device is rooted/jailbroken or exploited.
6
+ - Consequence: stolen tokens permit indefinite impersonation due to lack of backend token rotation.
7
+ - **Mitigation**: use **flutter_secure_storage**, which leverages Android Keystore/iOS Keychain with hardware-backed cryptographic enclaves.
8
+
9
+ Hardcoding API keys and credentials in Dart source code is another critical failing. Automated decompilation tools can extract these secrets from compiled binaries.
10
+ - **Mitigation**: inject secrets via secure environment variables during CI/CD, retrieve dynamically from backend secret management services.
11
+
12
+ ---
13
+
14
+ ### Sub-Clause 1.2: Cryptographic Implementation Flaws and Algorithmic Misuse
15
+ - Developers often misuse cryptographic primitives due to lack of expertise.
16
+ - Common flaws:
17
+ - Using weak PRNGs (`Math.random()`), leading to predictable entropy.
18
+ - Inconsistent RSA key lengths.
19
+ - Failure to destroy seeds post-generation.
20
+ - Vulnerable AES-CBC with PKCS padding without MACs → padding oracle attacks.
21
+ - **Mitigation**: use NIST-approved authenticated encryption (AES-GCM) via vetted packages (`encrypt`, `crypto`).
22
+
23
+ ---
24
+
25
+ ### Sub-Clause 1.3: Token Lifecycle and Session Management Deficiencies
26
+ - Refresh tokens not invalidated post-use.
27
+ - Access tokens not revoked on logout.
28
+ - Orphaned tokens hijacked by malware or physical access.
29
+ - **Mitigation**:
30
+ - Short-lived access tokens.
31
+ - Backend expiry enforcement.
32
+ - Immediate local purging and backend revocation on logout.
33
+ - Risk-based verification for sensitive actions.
34
+
35
+ ---
36
+
37
+ ### Cryptographic Vulnerability Table
38
+
39
+ | Vulnerability | Root Cause Mechanism | Architectural Remediation Strategy |
40
+ |-----------------------------|--------------------------------------------------------|------------------------------------|
41
+ | Plaintext Secret Exposure | SharedPreferences for API keys/tokens | Use flutter_secure_storage (Keystore/Keychain) |
42
+ | RNG Bias & Key Predictability | Dart Math.random() for entropy | Use secure PRNGs, destroy seeds |
43
+ | Padding Oracle Vulnerabilities | AES-CBC without MAC | Use AES-GCM via vetted packages |
44
+ | Orphaned Session Tokens | Tokens not purged/revoked on logout | Local purge + backend revocation |
45
+
46
+ ---
47
+
48
+ ## Clause II: Network Security and Transport Layer Exploitations
49
+
50
+ ### Sub-Clause 2.1: Certificate Pinning and MITM Attack Vectors
51
+ - Relying solely on device CA trust store is insecure.
52
+ - Malicious root certificates allow interception of HTTPS traffic.
53
+ - **Mitigation**: implement strict certificate pinning (e.g., `flutter_ssl_pinning`, `http_certificate_pinner`).
54
+
55
+ ---
56
+
57
+ ### Sub-Clause 2.2: Application-Layer Payload Encryption
58
+ - TLS alone is insufficient in zero-trust architectures.
59
+ - Sensitive payloads (KYC, banking, PII) must be encrypted before transmission.
60
+ - **Mitigation**: AES payload encryption with symmetric key coordination.
61
+
62
+ ---
63
+
64
+ ### Sub-Clause 2.3: Deep Link Hijacking & WebView Vulnerabilities
65
+ - Custom URL schemes without validation allow hijacking.
66
+ - Malicious apps intercept OAuth codes, reset tokens, etc.
67
+ - Developers often pipe unvalidated deep link parameters into SQLite, WebViews, or API calls → SQL injection, code execution.
68
+ - **Mitigation**: validate parameters against allowlists, verify authentication before rendering sensitive screens.
69
+
70
+ WebViews introduce XSS/CSRF risks if unrestricted JavaScript execution is enabled.
71
+ - **Mitigation**: sandbox WebViews, restrict JS interfaces, deny local storage access.
72
+
73
+ ---
74
+
75
+ ## Clause III: System Integrity, Reverse Engineering, and Native Interop Exploits
76
+
77
+ ### Sub-Clause 3.1: Obfuscation, Minification, and Tamper Resilience
78
+ - Failure to use `--obfuscate` and `--split-debug-info` exposes readable binaries.
79
+ - Lack of Runtime Application Self-Protection (RASP).
80
+ - **Mitigation**: enforce obfuscation, detect rooted/jailbroken devices, terminate execution in insecure environments.
81
+
82
+ ---
83
+
84
+ ### Sub-Clause 3.2: Native Memory Manipulation and FFI Vulnerabilities
85
+ - FFI bypasses Dart’s memory safety.
86
+ - Developers fail to deallocate native memory → leaks, buffer overflows, use-after-free.
87
+ - Linked binaries often lack protections (RELRO, stack canaries).
88
+ - **Mitigation**: strict manual deallocation, compile with fortified functions.
89
+
90
+ ---
91
+
92
+ ### Sub-Clause 3.3: Background Snapshots and Logging Hygiene
93
+ - Developers leave `print()` statements in production → leaks sensitive data via logcat/console.
94
+ - OS snapshots expose sensitive UI in app switcher.
95
+ - **Mitigation**: use `dart:developer log`, redact sensitive strings, enforce `FLAG_SECURE` (Android) or `applicationWillResignActive` (iOS).
96
+
97
+ ---
98
+
99
+ ### System Integrity Table
100
+
101
+ | Vector | Anti-Pattern | Mitigation Strategy |
102
+ |-----------------------------|---------------------------------------------|---------------------|
103
+ | Binary Reverse Engineering | No obfuscation | Use `--obfuscate`, `--split-debug-info` |
104
+ | Native Interop (FFI) | Unmanaged malloc, missing protections | Manual deallocation, enforce RELRO/stack canaries |
105
+ | Logcat/Console Leaks | `print()` for sensitive data | Use `dart:developer log`, redact |
106
+ | Background Snapshots | Sensitive UI visible in app switcher | Use FLAG_SECURE / lifecycle masking |
107
+
108
+ ---
109
+
110
+ ## Clause IV: Memory Management Deficiencies and Resource Leaks
111
+
112
+ ### Sub-Clause 4.1: Dart GC vs Native Resource Allocation
113
+ - Dart GC manages heap objects but not native resources.
114
+ - Abandoned Dart proxies → orphaned native resources (graphics buffers, sockets).
115
+ - **Mitigation**: explicitly destruct native resources.
116
+
117
+ ---
118
+
119
+ ### Sub-Clause 4.2: Controller, Listener, and Ticker Retention
120
+ - Failure to dispose controllers (AnimationController, TextEditingController, etc.) keeps them active post-widget removal.
121
+ - Leads to CPU drain, battery drain, GC retention.
122
+ - **Mitigation**: always call `.dispose()` in widget lifecycle.
123
+
124
+ ---
125
+
126
+ ### Sub-Clause 4.3: Unmanaged Stream Subscriptions & Async Context Retention
127
+ - StreamSubscriptions not canceled → background callbacks persist.
128
+ - Can crash app if `setState()` called on unmounted widget.
129
+ - **Mitigation**: assign subscriptions to variables, cancel in `dispose()`, use `StreamBuilder`.
130
+
131
+ Misuse of BuildContext across async gaps → runtime crashes.
132
+ - **Mitigation**: check `mounted` property before using context post-await.
133
+
134
+ ---
135
+
136
+ ### Sub-Clause 4.4: Timer Leaks and Background Isolate Memory
137
+ - Timers not canceled → callbacks persist indefinitely.
138
+ - Background isolates not closing network/database connections → orphaned sockets.
139
+ - **Mitigation**: cancel timers, close connections before isolate termination.
140
+
141
+ ---
142
+
143
+ ## Clause V: Memory Bloat and Asset Optimization Anti-Patterns
144
+
145
+ ### Sub-Clause 5.1: High-Resolution Bitmap Decoding and Heap Exhaustion
146
+ - When developers load standard, high-resolution images (such as 4K photographs from a device camera or a network CDN) into image widgets designed for small UI avatars or thumbnails, they trigger a catastrophic memory event.
147
+ - The Flutter engine does not natively scale the image down before placing it in memory; instead, it mathematically decodes the entire high-resolution image into a raw, uncompressed bitmap directly into the device's precious RAM.
148
+ - A single, unoptimized multi-megapixel image can effortlessly consume tens to hundreds of megabytes of memory.
149
+ - If these unoptimized images are rendered within an infinite ListView without aggressive lazy loading, the rapid, successive instantiation of these massive bitmaps instantaneously overwhelms the heap, leading to Out-Of-Memory (OOM) crashes.
150
+
151
+ ---
152
+
153
+ ### Sub-Clause 5.2: Asset Preloading and Cache Saturation
154
+ - Developers frequently preload massive asset bundles (images, fonts, audio files) into memory at application startup.
155
+ - While intended to reduce runtime latency, this practice saturates the cache and consumes memory unnecessarily.
156
+ - **Mitigation**: implement lazy loading strategies, load assets only when required, and leverage caching libraries with eviction policies.
157
+
158
+ ---
159
+
160
+ ### Sub-Clause 5.3: Inefficient Image Resizing and Format Misuse
161
+ - Using uncompressed formats (e.g., PNG) for large assets instead of optimized formats (e.g., WebP, JPEG) leads to excessive memory consumption.
162
+ - Developers often resize images at runtime rather than preprocessing them, causing repeated decoding overhead.
163
+ - **Mitigation**: preprocess images to appropriate resolutions, use efficient formats, and apply compression pipelines before bundling.
164
+
165
+ ---
166
+
167
+ ### Sub-Clause 5.4: Animated Asset Overconsumption
168
+ - Excessive use of GIFs or unoptimized Lottie animations consumes CPU and memory.
169
+ - Continuous decoding of animation frames leads to performance degradation.
170
+ - **Mitigation**: prefer vector-based animations, optimize frame rates, and limit animation scope.
171
+
172
+ ---
173
+
174
+ ### Memory Bloat Table
175
+
176
+ | Anti-Pattern | Root Cause Mechanism | Mitigation Strategy |
177
+ |----------------------------------|-----------------------------------------------|---------------------|
178
+ | High-Res Bitmap Decoding | Full 4K decode into RAM | Pre-scale images, lazy load |
179
+ | Asset Preloading | Loading all assets at startup | Lazy load, cache eviction |
180
+ | Format Misuse | PNG for large assets | Use WebP/JPEG, preprocess |
181
+ | Animated Asset Overconsumption | GIF/Lottie overuse | Optimize animations, prefer vectors |
182
+
183
+ ---
184
+
185
+ ## Clause VI: State Management Fragmentation
186
+
187
+ ### Sub-Clause 6.1: Improper Global State Retention
188
+ - Developers often rely on global variables or singletons to manage state.
189
+ - This leads to memory retention, race conditions, and unpredictable behavior.
190
+ - **Mitigation**: adopt structured state management frameworks (e.g., Provider, Riverpod, Bloc).
191
+
192
+ ---
193
+
194
+ ### Sub-Clause 6.2: Redundant Rebuilds and Render-Tree Thrashing
195
+ - Misuse of `setState()` triggers unnecessary widget rebuilds.
196
+ - Causes UI jank, battery drain, and degraded performance.
197
+ - **Mitigation**: use `ValueNotifier`, `ChangeNotifier`, or memoization to minimize rebuilds.
198
+
199
+ ---
200
+
201
+ ### Sub-Clause 6.3: Fragmented State Across Multiple Layers
202
+ - Mixing multiple state management solutions (e.g., Redux + Provider + custom streams) creates fragmentation.
203
+ - Leads to debugging complexity and inconsistent lifecycle handling.
204
+ - **Mitigation**: enforce a single, unified state management approach.
205
+
206
+ ---
207
+
208
+ ## Clause VII: Continuous Integration and Governance Failures
209
+
210
+ ### Sub-Clause 7.1: Insecure CI/CD Pipelines
211
+ - Secrets often hardcoded in pipeline scripts.
212
+ - Build artifacts not signed or verified.
213
+ - **Mitigation**: use secret managers, enforce artifact signing, and implement reproducible builds.
214
+
215
+ ---
216
+
217
+ ### Sub-Clause 7.2: Lack of Automated Security Testing
218
+ - Developers neglect static analysis, dependency scanning, and penetration testing.
219
+ - Vulnerabilities persist into production.
220
+ - **Mitigation**: integrate automated tools (e.g., SonarQube, OWASP Dependency-Check) into CI/CD.
221
+
222
+ ---
223
+
224
+ ### Sub-Clause 7.3: Insufficient Governance Policies
225
+ - Teams fail to enforce coding standards, secure review processes, and compliance audits.
226
+ - **Mitigation**: establish governance frameworks, enforce peer review, and conduct periodic audits.
227
+
228
+ ---
229
+
230
+ ## Clause VIII: Advanced Threat Modeling and Enterprise Recommendations
231
+
232
+ ### Sub-Clause 8.1: Threat Modeling Oversights
233
+ - Developers rarely conduct structured threat modeling for Flutter apps.
234
+ - Attack surfaces (FFI, deep links, WebViews) remain unaddressed.
235
+ - **Mitigation**: adopt STRIDE/DREAD methodologies, simulate adversarial scenarios.
236
+
237
+ ---
238
+
239
+ ### Sub-Clause 8.2: Enterprise-Grade Security Baselines
240
+ - Enterprises must enforce:
241
+ - Hardware-backed secure storage.
242
+ - Certificate pinning.
243
+ - Obfuscation and RASP.
244
+ - Memory leak detection tools.
245
+ - These baselines ensure resilience against systemic vulnerabilities.
246
+
247
+ ---
@@ -0,0 +1,83 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { hostname, userInfo, platform, arch } from 'node:os';
3
+ import { readFileSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { homedir } from 'node:os';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { dirname } from 'node:path';
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
11
+
12
+ const POSTHOG_ENDPOINT = 'https://us.i.posthog.com/capture';
13
+ const POSTHOG_API_KEY = 'phc_JcSoYrxMXvJjCtlePCFI6UuhNbwq33WXNeUKmDB6Msz';
14
+
15
+ // ── Opt-out check (lazy, cached) ──
16
+
17
+ let _disabled = null;
18
+
19
+ function isDisabled() {
20
+ if (_disabled !== null) return _disabled;
21
+
22
+ const envVal = (process.env.SHIPLI_TELEMETRY || '').toLowerCase();
23
+ if (['off', 'false', '0', 'no'].includes(envVal)) {
24
+ _disabled = true;
25
+ return true;
26
+ }
27
+
28
+ if (process.env.DO_NOT_TRACK === '1') {
29
+ _disabled = true;
30
+ return true;
31
+ }
32
+
33
+ try {
34
+ const raw = readFileSync(join(homedir(), '.shipli'), 'utf-8');
35
+ const config = JSON.parse(raw);
36
+ if (config.telemetry === false) {
37
+ _disabled = true;
38
+ return true;
39
+ }
40
+ } catch {
41
+ // No config file or invalid JSON — telemetry stays on
42
+ }
43
+
44
+ _disabled = false;
45
+ return false;
46
+ }
47
+
48
+ // ── Anonymous device ID ──
49
+
50
+ function getAnonymousId() {
51
+ try {
52
+ const raw = `${hostname()}:${userInfo().username}`;
53
+ return createHash('sha256').update(raw).digest('hex').slice(0, 16);
54
+ } catch {
55
+ return createHash('sha256').update(hostname()).digest('hex').slice(0, 16);
56
+ }
57
+ }
58
+
59
+ // ── Fire-and-forget event dispatch ──
60
+
61
+ export function trackEvent(name, properties = {}) {
62
+ if (isDisabled()) return;
63
+
64
+ const payload = {
65
+ api_key: POSTHOG_API_KEY,
66
+ event: name,
67
+ distinct_id: getAnonymousId(),
68
+ properties: {
69
+ ...properties,
70
+ cli_version: pkg.version,
71
+ node_version: process.version,
72
+ os: platform(),
73
+ arch: arch(),
74
+ },
75
+ };
76
+
77
+ fetch(POSTHOG_ENDPOINT, {
78
+ method: 'POST',
79
+ headers: { 'Content-Type': 'application/json' },
80
+ body: JSON.stringify(payload),
81
+ signal: AbortSignal.timeout(3000),
82
+ }).catch(() => {});
83
+ }