Skip to content
Open
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
10 changes: 7 additions & 3 deletions docs/backend/backend_python/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1117,9 +1117,14 @@
"in": "query",
"required": false,
"schema": {
"$ref": "#/components/schemas/InputType",
"allOf": [
{
"$ref": "#/components/schemas/InputType"
}
],
"description": "Choose input type: 'path' or 'base64'",
"default": "path"
"default": "path",
"title": "Input Type"
},
"description": "Choose input type: 'path' or 'base64'"
}
Expand Down Expand Up @@ -2199,7 +2204,6 @@
"metadata": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
Expand Down
12 changes: 12 additions & 0 deletions frontend/src-tauri/2
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

up to date, audited 901 packages in 3s

165 packages are looking for funding
run `npm fund` for details

1 moderate severity vulnerability

To address all issues, run:
npm audit fix

Run `npm audit` for details.
Comment on lines +1 to +12
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove/relocate this accidental audit artifact file.
A file named frontend/src-tauri/2 with plain npm-audit output looks unintentional and doesn’t belong in src-tauri. Consider deleting it or moving it to a clearly named docs location if you want to track audit status.

🤖 Prompt for AI Agents
In frontend/src-tauri/2 (lines 1-12): this file contains accidental npm-audit
output and should not live in src-tauri; remove the file from the repository
(git rm) or move it to a proper docs or assets location with a clear name (e.g.,
docs/audit-output/npm-audit-YYYYMMDD.txt), update any references, and commit the
change; if you want to preserve audit logs going forward, add a process or
.gitignore entry so transient audit outputs aren’t accidentally committed.

2 changes: 1 addition & 1 deletion frontend/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ tauri-plugin-dialog = "2.4.2"
tauri-plugin-process = "2.3.1"
tauri-plugin-store = "2.4.1"
tauri-plugin-updater = "2.9.0"
tauri-plugin-opener = "2.5.2"
tauri-plugin-opener = "2"

[features]
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
Expand Down
26 changes: 24 additions & 2 deletions frontend/src-tauri/capabilities/migrated.json
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,28 @@
"fs:default",
"dialog:default",
"store:default",
"opener:allow-reveal-item-in-dir"
"opener:allow-reveal-item-in-dir",
"opener:allow-open-path",
"opener:allow-open-url",
"opener:allow-default-urls",
{
"identifier": "opener:allow-open-path",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "opener:allow-open-url",
"allow": [
{
"url": "https://*"
},
{
"url": "http://*"
}
]
}
Comment on lines +139 to +161
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the migrated.json file structure
echo "=== Migrated.json opener permissions ===" 
rg -n "opener:" frontend/src-tauri/capabilities/migrated.json -A1 -B1

echo ""
echo "=== Search for opener plugin imports ===" 
rg -r '@tauri-apps/plugin-opener' frontend/src --type ts --type tsx --type js

echo ""
echo "=== Search for openUrl calls ===" 
rg -n 'openUrl\s*\(' frontend/src --type ts --type tsx --type js -A2 -B2 || echo "No openUrl calls found"

echo ""
echo "=== Search for openPath calls ===" 
rg -n 'openPath\s*\(' frontend/src --type ts --type tsx --type js -A2 -B2 || echo "No openPath calls found"

echo ""
echo "=== Search for http:// URL patterns in frontend ===" 
rg 'http://' frontend/src --type ts --type tsx --type js | head -20

echo ""
echo "=== Search for getVersion or URL scheme patterns ===" 
rg 'allow-default-urls' frontend/src-tauri/capabilities/ -B5 -A5 || echo "No default-urls context"

Repository: AOSSIE-Org/PictoPy

Length of output: 1614


🏁 Script executed:

#!/bin/bash
echo "=== Search src-tauri Rust files for opener usage ===" 
find frontend/src-tauri -name "*.rs" -type f | head -20

echo ""
echo "=== Search for opener in Rust code ===" 
rg -n 'opener' frontend/src-tauri --type rust -i -A3 -B3 2>/dev/null || echo "No opener matches in Rust"

echo ""
echo "=== Search for openPath or openUrl in Rust ===" 
rg -n 'open_path|openPath|open_url|openUrl' frontend/src-tauri -i -A3 -B3 2>/dev/null || echo "No openPath/openUrl matches"

echo ""
echo "=== Check file structure in src-tauri ===" 
ls -la frontend/src-tauri/ | head -30

echo ""
echo "=== Check for any references to http in config ===" 
rg 'http://' frontend/src-tauri/capabilities/ -B2 -A2

Repository: AOSSIE-Org/PictoPy

Length of output: 2282


🏁 Script executed:

#!/bin/bash
echo "=== Search for Tauri command handlers or invoke patterns ===" 
rg -n '#\[command\]|#\[tauri::command\]' frontend/src-tauri/src -A5 2>/dev/null | head -40

echo ""
echo "=== Search for any async invoke or command patterns ===" 
rg -n 'invoke\s*\(|Command|command' frontend/src-tauri/src --type rust -i -A2 | head -40

echo ""
echo "=== Check what lib.rs exports ===" 
cat -n frontend/src-tauri/src/lib.rs

echo ""
echo "=== Check README or docs for opener context ===" 
find . -name "README*" -o -name "*.md" | xargs grep -l 'opener\|open.*file\|open.*url' 2>/dev/null | head -5

echo ""
echo "=== Look at git history for migrated.json changes ===" 
git log --oneline frontend/src-tauri/capabilities/migrated.json 2>/dev/null | head -10

echo ""
echo "=== Check app purpose - look at tauri.conf ===" 
cat frontend/src-tauri/tauri.conf.json

Repository: AOSSIE-Org/PictoPy

Length of output: 3068


🏁 Script executed:

#!/bin/bash
echo "=== Search for invocations through Tauri API calls ===" 
rg -n 'invoke.*opener|window\.__TAURI__' frontend/src --type js -A3 -B1 2>/dev/null | head -50

echo ""
echo "=== Search for references to issue 688 ===" 
rg -rn '688|Open Original File|reveal.*item' frontend/src-tauri frontend/src --type js --type rust 2>/dev/null | head -20

echo ""
echo "=== Check frontend structure ===" 
find frontend -name "*.ts" -o -name "*.tsx" | grep -v node_modules | head -20

echo ""
echo "=== Search main frontend files for any opener/url/path opens ===" 
find frontend/src -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" \) ! -path "*/node_modules/*" | head -10 | xargs cat 2>/dev/null | grep -i 'open\|url\|path' | head -30

Repository: AOSSIE-Org/PictoPy

Length of output: 1351


Tighten opener permissions: open-path with "**" and http://* are overly permissive for a photo management app.

The feature enables "Open Original File" (#688) but the allowlists are unnecessarily broad:

  • path: "**" allows opening any file on the system; restrict to user home directory, Pictures folder, or user-selected library roots.
  • http://* creates a MITM risk and is likely unnecessary (photo apps need only local file access or HTTPS for remote images).
  • allow-default-urls should be removed unless explicitly required.

If the webview is exposed to XSS (the app's CSP asset scope is already broad with scope: ["**"]), these permissions increase impact. Tighten to minimum viable access.

]
}
}
31 changes: 16 additions & 15 deletions frontend/src/components/Media/MediaInfoPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { open } from '@tauri-apps/plugin-shell';
import { openPath, openUrl } from '@tauri-apps/plugin-opener';
import {
X,
ImageIcon as ImageLucide,
Expand All @@ -26,6 +26,18 @@ export const MediaInfoPanel: React.FC<MediaInfoPanelProps> = ({
currentIndex,
totalImages,
}) => {
const handleLocationClick = async () => {
if (currentImage?.metadata?.latitude && currentImage?.metadata?.longitude) {
const { latitude, longitude } = currentImage.metadata;
const url = `https://maps.google.com/?q=${latitude},${longitude}`;
try {
await openUrl(url);
} catch (error) {
console.error('Failed to open map URL:', error);
}
}
};

const getFormattedDate = () => {
if (currentImage?.metadata?.date_created) {
return new Date(currentImage.metadata.date_created).toLocaleDateString(
Expand All @@ -46,18 +58,6 @@ export const MediaInfoPanel: React.FC<MediaInfoPanelProps> = ({
return currentImage.path?.split(/[/\\]/).pop() || 'Image';
};

const handleLocationClick = async () => {
if (currentImage?.metadata?.latitude && currentImage?.metadata?.longitude) {
const { latitude, longitude } = currentImage.metadata;
const url = `https://maps.google.com/?q=${latitude},${longitude}`;
try {
await open(url);
} catch (error) {
console.error('Failed to open map URL:', error);
}
}
};

if (!show) return null;

return (
Expand Down Expand Up @@ -163,17 +163,18 @@ export const MediaInfoPanel: React.FC<MediaInfoPanelProps> = ({
onClick={async () => {
if (currentImage?.path) {
try {
await open(currentImage.path);
await openPath(currentImage.path);
} catch (error) {
console.error('Failed to open file:', error);
}
}
}}
>
Open Original File
<SquareArrowOutUpRight className="ml-2 inline h-4 w-4" />
</button>
</div>
</div>
</div>
);
};
};