Implement controlled app launch (T-401)

This commit is contained in:
ila
2026-07-19 21:10:59 +08:00
parent d0cf333394
commit 87083c387f
32 changed files with 1802 additions and 31 deletions
+55 -6
View File
@@ -39,12 +39,16 @@ type InstalledFile struct {
// InstalledApp is the local installed-app.json v1 protocol.
type InstalledApp struct {
SchemaVersion int `json:"schema_version"`
ID string `json:"id"`
Version string `json:"version"`
Architecture string `json:"architecture"`
Channel string `json:"channel"`
Files []InstalledFile `json:"files"`
SchemaVersion int `json:"schema_version"`
ID string `json:"id"`
Version string `json:"version"`
Architecture string `json:"architecture"`
Channel string `json:"channel"`
Entrypoint string `json:"entrypoint,omitempty"`
WorkingDirectory string `json:"working_directory,omitempty"`
MinOS string `json:"min_os,omitempty"`
RequiresAdmin bool `json:"requires_admin"`
Files []InstalledFile `json:"files"`
}
// InstallationSnapshot contains disk facts without deriving UI status.
@@ -107,6 +111,37 @@ func (store *InstalledAppStore) Read(appID string) (record InstalledApp, found b
return store.readLocked(appID)
}
// ResolveCurrent returns a validated record and the real current directory
// below its app root. It never creates directories and rejects unsafe layouts.
func (store *InstalledAppStore) ResolveCurrent(appID string) (InstalledApp, string, error) {
store.mu.Lock()
defer store.mu.Unlock()
record, found, err := store.readLocked(appID)
if err != nil {
return InstalledApp{}, "", err
}
if !found {
return InstalledApp{}, "", os.ErrNotExist
}
appRoot, exists, err := store.inspectAppRoot(appID)
if err != nil {
return InstalledApp{}, "", err
}
if !exists {
return InstalledApp{}, "", os.ErrNotExist
}
current := filepath.Join(appRoot, "current")
if err := requireRealDirectory(current); err != nil {
if errors.Is(err, os.ErrNotExist) {
return InstalledApp{}, "", fmt.Errorf("%w: current directory is missing: %w", ErrStorageLayoutUnsafe, err)
}
return InstalledApp{}, "", err
}
record.Files = append([]InstalledFile(nil), record.Files...)
return record, current, nil
}
// Inspect reads the installed record and detects an unfinished install journal.
func (store *InstalledAppStore) Inspect(appID string) (InstallationSnapshot, error) {
store.mu.Lock()
@@ -283,6 +318,20 @@ func (record InstalledApp) validate() error {
if record.Channel != "stable" {
return fmt.Errorf("%w: channel=%q", ErrInstalledAppInvalid, record.Channel)
}
if record.Entrypoint != "" {
if err := safepath.ValidateRelative(record.Entrypoint); err != nil {
return fmt.Errorf("%w: entrypoint: %v", ErrInstalledAppInvalid, err)
}
}
if record.WorkingDirectory != "" && record.WorkingDirectory != "." {
if err := safepath.ValidateRelative(record.WorkingDirectory); err != nil {
return fmt.Errorf("%w: working_directory: %v", ErrInstalledAppInvalid, err)
}
}
if record.MinOS != "" && record.MinOS != "windows-7-sp1" &&
record.MinOS != "windows-10" && record.MinOS != "windows-11" {
return fmt.Errorf("%w: min_os=%q", ErrInstalledAppInvalid, record.MinOS)
}
if record.Files == nil {
return fmt.Errorf("%w: files must be an array", ErrInstalledAppInvalid)
}