Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ body:
attributes:
label: Privacy
description: >-
FOSScanner makes no network calls of its own and keeps in-progress
pages in memory (the OS may cache a shared PDF; see the README).
Please confirm your suggestion doesn't require breaking that.
FOSScanner makes no network calls of its own. On native platforms it
keeps an unfinished draft in the app's private, OS-managed cache; web
drafts are memory-only (see the README). Please confirm your suggestion
preserves these privacy boundaries.
options:
- label: This doesn't require adding network calls or persistent storage of scanned content
- label: This doesn't require network calls or storage outside the app-private cache
required: true
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ and this page list.*
- Re-edit any page after the fact (corners, filter, rotation, brightness,
and contrast) without re-scanning
- Capture multiple pages in sequence and reorder them with drag-and-drop
- Automatically restore an unfinished draft after a native app restart
- Import existing photos from your gallery instead of (or alongside)
capturing new ones
- Combine captured pages into a single PDF
Expand All @@ -62,8 +63,19 @@ used as-is there.
- All image processing and PDF generation happens on-device.
- Imported gallery originals are never modified or deleted. App-owned camera
temp files are removed after the app attempts to copy their bytes into
memory (including failed reads), and in-progress pages remain only in memory
until the app is closed.
memory (including failed reads).
- On native platforms, unfinished drafts are saved automatically in the app's
private, OS-managed application cache. They survive normal process death and
app restarts, but caches are transient and can be purged by the OS under
storage pressure; they are not durable storage or included in Android OS
backups. A draft includes both original and processed page images plus
crop/filter/edit settings. Atomic staging plus one backup generation can
retain the current and immediately previous draft contents. Draft data is
also removed when you confirm **Clear all**, choose **Clear draft** after
sharing, clear the app's storage, or uninstall the app. Sharing does not
delete a draft unless you explicitly choose that option.
- The web build does not persist drafts; its in-progress pages remain only in
memory and disappear when the page is closed or reloaded.
- PDF sharing starts from in-memory bytes. Depending on the platform,
`share_plus` may materialize a copy in the app/OS cache for the receiving app;
that cache is OS-managed and is not guaranteed to disappear immediately
Expand Down
10 changes: 7 additions & 3 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ a private discussion with maintainers before anything becomes public.

Given the app's design — all image processing and PDF generation happens
on-device, and it makes no network requests of its own (see the README's
"Privacy" section) — the most relevant classes of report are:
"Privacy" section) — the most relevant classes of report are below. Automatic
native drafts use the app-private, OS-managed application cache: they survive
normal process death and restarts, but can be purged under storage pressure and
are excluded from Android OS backup.

- Anything that would let a malicious document/image trigger memory
corruption or a crash via the native OpenCV pipeline
(`document_processor_native.dart`)
- Anything that would cause photos or generated PDFs to be persisted or
leaked when they shouldn't be (see the README's privacy guarantees)
- Anything that would expose app-private automatic draft files, retain them
after an explicit clear, or cause photos/generated PDFs to be persisted or
leaked outside the retention behavior documented in the README

Dependency vulnerabilities are also welcome as reports, though Dependabot
already opens PRs for those automatically where a fix is available.
3 changes: 2 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
<application
android:label="FOSScanner"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
android:icon="@mipmap/ic_launcher"
android:allowBackup="false">
<activity
android:name=".MainActivity"
android:exported="true"
Expand Down
6 changes: 4 additions & 2 deletions fastlane/metadata/android/en-US/full_description.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Privacy:
* All image processing and PDF generation happens on-device
* Imported gallery originals are never modified or deleted. App-owned camera temp files
are removed after the app attempts to copy them into memory, including failed reads
* In-progress pages stay in memory. Sharing a PDF may create a copy in an OS-managed cache
for the receiving app, and the OS decides when that cache is removed
* An unfinished draft is saved automatically in the app's private, OS-managed cache so it
can be restored after an app restart. Clearing the document removes it; after sharing, you
choose whether to clear or keep the draft. The OS may evict cached data, and sharing may
create a separate cached PDF copy
* The app makes no network requests of its own
9 changes: 6 additions & 3 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import 'package:flutter/material.dart';

import 'screens/scanner_home_page.dart';
import 'services/draft_store.dart';

void main() {
runApp(const FOSScannerApp());
runApp(FOSScannerApp(draftStore: createDraftStore()));
}

class FOSScannerApp extends StatelessWidget {
const FOSScannerApp({super.key});
const FOSScannerApp({super.key, this.draftStore});

final DraftStore? draftStore;

@override
Widget build(BuildContext context) {
Expand All @@ -28,7 +31,7 @@ class FOSScannerApp extends StatelessWidget {
),
),
themeMode: ThemeMode.system,
home: const ScannerHomePage(),
home: ScannerHomePage(draftStore: draftStore ?? const NoOpDraftStore()),
);
}
}
Loading
Loading