Skip to content

fix: sync generated autostart desktop entries#371

Open
yixinshark wants to merge 1 commit into
linuxdeepin:masterfrom
yixinshark:fix/sync-generated-autostart
Open

fix: sync generated autostart desktop entries#371
yixinshark wants to merge 1 commit into
linuxdeepin:masterfrom
yixinshark:fix/sync-generated-autostart

Conversation

@yixinshark

@yixinshark yixinshark commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Refresh AM-generated autostart desktop files when application desktop entries are reloaded.
  • Avoid reusing stale in-memory autostart entries after the generated file is deleted.
  • Preserve the current Hidden value from disk while syncing generated autostart entries.

Test plan

  • cmake --build build --target dde-application-manager

Summary by Sourcery

Synchronize and persist generated autostart desktop entries with their source application entries while ensuring file existence checks and error handling are improved.

New Features:

  • Introduce synchronization of generated autostart entries with updated application desktop entries, preserving user Hidden state.

Bug Fixes:

  • Avoid using stale in-memory autostart entries when the underlying autostart desktop file has been removed.
  • Ensure generated autostart entries are refreshed when application desktop entries are reloaded.

Enhancements:

  • Add reusable helpers for checking autostart source file existence, detecting generated autostart sources, and saving autostart desktop entries.
  • Improve autostart toggling logic to reuse existing non-generated entries safely and emit errors on failed writes.

@sourcery-ai

sourcery-ai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds robust handling and resync of generated autostart desktop entries, ensuring files exist before use, reusing only valid existing entries, preserving Hidden state, and centralizing autostart file I/O.

Sequence diagram for syncing generated autostart desktop entries

sequenceDiagram
    participant ApplicationManager1Service
    participant ApplicationService

    ApplicationManager1Service->>ApplicationService: setAutostartSource(AutostartSource)
    ApplicationManager1Service->>ApplicationService: syncGeneratedAutostartEntry()

    ApplicationService->>ApplicationService: autostartSourceFileExists()
    alt [autostartSourceFileExists]
        ApplicationService->>ApplicationService: QFile::open(ReadOnly | Text)
        ApplicationService->>ApplicationService: DesktopEntry::parse()
        ApplicationService->>ApplicationService: hasGeneratedAutostartSource()
        alt [hasGeneratedAutostartSource]
            ApplicationService->>ApplicationService: DesktopEntry::insert(XDeepinGenerateSource)
            ApplicationService->>ApplicationService: DesktopEntry::insert(Hidden)
            ApplicationService->>ApplicationService: saveAutostartEntry(fileName, entry)
            ApplicationService->>ApplicationService: setAutostartSource(AutostartSource)
        end
    end
Loading

File-Level Changes

Change Details Files
Guard autostart checks and reuse logic with file-existence and generated-source detection.
  • Replace empty-path check in isAutoStart with a file-existence helper that also verifies the path is non-empty.
  • Introduce autostartSourceFileExists() to encapsulate QFile::exists checks for the current autostart source path.
  • Introduce hasGeneratedAutostartSource() to detect entries containing the X-Deepin generate source key and use it to gate reuse of in-memory autostart entries.
src/dbus/applicationservice.cpp
src/dbus/applicationservice.h
Centralize autostart entry persistence to avoid duplicated I/O and improve error handling.
  • Add saveAutostartEntry() helper that writes a DesktopEntry to disk with truncation, flush, and logging on error.
  • Refactor setAutoStart to use saveAutostartEntry instead of ad-hoc QFile writing, returning a D-Bus error reply when saving fails.
  • Ensure setAutostartSource is only called after a successful write to keep in-memory state consistent with disk.
src/dbus/applicationservice.cpp
src/dbus/applicationservice.h
Synchronize generated autostart entries with updated application desktop entries while preserving Hidden state.
  • Add syncGeneratedAutostartEntry() that reloads the current autostart file, validates it is a generated entry, and rebuilds it from the current application DesktopEntry.
  • Copy over the X-Deepin generate source field from the current desktop source into the new autostart entry.
  • Preserve the current Hidden value from disk when regenerating the autostart entry and update the in-memory autostart source accordingly.
  • Invoke syncGeneratedAutostartEntry() when autostart sources are updated or applications are reloaded to keep generated autostart files in sync.
src/dbus/applicationservice.cpp
src/dbus/applicationmanager1service.cpp
src/dbus/applicationservice.h

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • The logic for detecting generated autostart entries is duplicated between hasGeneratedAutostartSource() and syncGeneratedAutostartEntry() (manual group->get().contains(...) check); consider reusing hasGeneratedAutostartSource() inside syncGeneratedAutostartEntry() to keep this condition centralized and less error-prone.
  • syncGeneratedAutostartEntry() silently returns on several error paths (open/parse/validation failures) with only a generic warning; if these cases are important to diagnose in production, consider including the file path and a brief reason in each log to make it easier to trace why sync was skipped.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The logic for detecting generated autostart entries is duplicated between `hasGeneratedAutostartSource()` and `syncGeneratedAutostartEntry()` (manual `group->get().contains(...)` check); consider reusing `hasGeneratedAutostartSource()` inside `syncGeneratedAutostartEntry()` to keep this condition centralized and less error-prone.
- `syncGeneratedAutostartEntry()` silently returns on several error paths (open/parse/validation failures) with only a generic warning; if these cases are important to diagnose in production, consider including the file path and a brief reason in each log to make it easier to trace why sync was skipped.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@yixinshark yixinshark force-pushed the fix/sync-generated-autostart branch 4 times, most recently from 10def6f to 93f2f45 Compare June 24, 2026 01:58
BLumia
BLumia previously approved these changes Jun 24, 2026
Comment thread src/dbus/applicationservice.cpp
@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

★ 总体评分:40分

■ 【总体评价】

代码实现了自启动状态同步与文件写入逻辑的优化,但存在严重的符号链接竞态条件安全漏洞
逻辑正确且代码质量良好,但因存在高危TOCTOU漏洞触发安全一票否决,强制降至上限40分

■ 【详细分析】

  • 1.语法逻辑(完全正确)✓

代码在isAutoStart中增加了文件存在性校验,在setAutoStart中增加了m_entry空指针保护,syncGeneratedAutostartEntry中正确解析并合并了Hidden状态与生成来源标记,saveAutostartEntry对写入字节数与刷新结果进行了严格校验,整体逻辑严密无语法错误

  • 2.代码质量(良好)✓

提取了saveAutostartEntry公共方法消除了原有的重复写入代码,shouldReuseAutostartEntry逻辑清晰表达了复用非自动生成自启动文件的意图,增加了拒绝覆盖符号链接的安全意识设计,函数职责划分合理

  • 3.代码性能(无性能问题)✓

syncGeneratedAutostartEntry对同一文件进行了一次读一次写属于业务必需开销,autostartSourceFileExists调用QFile::exists产生一次系统调用,在自启动管理这种低频触发场景下毫无性能负担

  • 4.代码安全(存在 1 个安全漏洞)✕

漏洞对比统计:新增漏洞 1 个,减少漏洞 0 个,持平 0 个
整体存在利用时间差进行本地权限提升的风险,攻击面在于自启动文件的写入流程

  • 安全漏洞1【高危】:TOCTOU竞态条件 在 saveAutostartEntry 函数中,攻击者可在 isSymLink 检查与 QFile::open 调用之间的极短时间窗口内,将目标普通文件替换为指向 /etc/shadow 或 /etc/sudoers 等敏感系统文件的符号链接,导致后续的 Truncate 写入操作覆盖系统关键文件,实现本地权限提升或拒绝服务 ——非常重要

  • 建议:废弃先检查后使用的非原子化操作,改用 Linux 底层的 O_NOFOLLOW 标志在打开文件时直接拒绝符号链接,彻底消除时间差

■ 【改进建议代码示例】

+++ b/src/dbus/applicationservice.cpp
@@ -15,6 +15,10 @@
 #include <QFileInfo>
 #include <QLoggingCategory>
 #include <QDir>
+#include <fcntl.h>
+#include <unistd.h>
+#include <cerrno>
+#include <cstring>
 
 Q_LOGGING_CATEGORY(appServiceLog, "deepin.app.service", QtWarningMsg)
 
@@ -961,14 +965,13 @@ bool ApplicationService::autostartCheck() const noexcept
 
 bool ApplicationService::saveAutostartEntry(const QString &fileName, const DesktopEntry &entry) noexcept
 {
-    const QFileInfo autostartFileInfo{fileName};
-    if (autostartFileInfo.isSymLink()) {
-        qWarning() << "refuse to overwrite symlink autostart file:" << fileName;
+    // 使用 O_NOFOLLOW 原子化防止 TOCTOU 竞态条件导致的符号链接攻击
+    int fd = ::open(fileName.toLocal8Bit().constData(), O_WRONLY | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0644);
+    if (fd == -1) {
+        qWarning() << "open file" << fileName << "failed:" << strerror(errno);
         return false;
     }
 
-    QFile autostartFile{fileName};
-    if (!autostartFile.open(QFile::WriteOnly | QFile::Text | QFile::Truncate)) {
-        qWarning() << "open file" << fileName << "failed:" << autostartFile.error();
+    if (!autostartFile.open(fd, QIODevice::WriteOnly | QIODevice::Text)) {
+        qWarning() << "bind fd to QFile failed:" << fileName;
+        ::close(fd);
         return false;
     }

Refresh generated autostart desktop files when application desktop entries are reloaded.
- Avoid reusing stale in-memory autostart entries after the user deletes the generated file.
- Rewrite AM-generated autostart entries from the current installed desktop entry.
- Preserve the current Hidden value from disk so disabled autostart entries stay disabled.
- Refuse to overwrite symlink autostart files and guard null desktop entries before syncing.

刷新生成的自启动 desktop 文件,使其跟随应用 desktop 更新。
- 用户删除生成文件后,避免继续复用内存中的旧自启动 entry。
- 对 AM 生成的自启动 entry,使用当前安装 desktop entry重新写入。
- 保留磁盘当前 Hidden 值,确保已禁用的自启动项不会被重新启用。
- 拒绝覆盖符号链接形式的自启动文件,并在同步前防御空 desktop entry。

Log: sync generated autostart desktop entries
Pms: BUG-367265
Change-Id: I8a0e0733aa755d9481a0a2bf5f5ae0b72741d507
@yixinshark yixinshark force-pushed the fix/sync-generated-autostart branch from 1980ffa to c437b17 Compare June 24, 2026 02:41
@yixinshark

Copy link
Copy Markdown
Contributor Author

deepin pr auto review

★ 总体评分:40分

■ 【总体评价】

代码实现了自启动状态同步与文件写入逻辑的优化,但存在严重的符号链接竞态条件安全漏洞
逻辑正确且代码质量良好,但因存在高危TOCTOU漏洞触发安全一票否决,强制降至上限40分

■ 【详细分析】

  • 1.语法逻辑(完全正确)✓

代码在isAutoStart中增加了文件存在性校验,在setAutoStart中增加了m_entry空指针保护,syncGeneratedAutostartEntry中正确解析并合并了Hidden状态与生成来源标记,saveAutostartEntry对写入字节数与刷新结果进行了严格校验,整体逻辑严密无语法错误

  • 2.代码质量(良好)✓

提取了saveAutostartEntry公共方法消除了原有的重复写入代码,shouldReuseAutostartEntry逻辑清晰表达了复用非自动生成自启动文件的意图,增加了拒绝覆盖符号链接的安全意识设计,函数职责划分合理

  • 3.代码性能(无性能问题)✓

syncGeneratedAutostartEntry对同一文件进行了一次读一次写属于业务必需开销,autostartSourceFileExists调用QFile::exists产生一次系统调用,在自启动管理这种低频触发场景下毫无性能负担

  • 4.代码安全(存在 1 个安全漏洞)✕

漏洞对比统计:新增漏洞 1 个,减少漏洞 0 个,持平 0 个
整体存在利用时间差进行本地权限提升的风险,攻击面在于自启动文件的写入流程

  • 安全漏洞1【高危】:TOCTOU竞态条件 在 saveAutostartEntry 函数中,攻击者可在 isSymLink 检查与 QFile::open 调用之间的极短时间窗口内,将目标普通文件替换为指向 /etc/shadow 或 /etc/sudoers 等敏感系统文件的符号链接,导致后续的 Truncate 写入操作覆盖系统关键文件,实现本地权限提升或拒绝服务 ——非常重要
  • 建议:废弃先检查后使用的非原子化操作,改用 Linux 底层的 O_NOFOLLOW 标志在打开文件时直接拒绝符号链接,彻底消除时间差

■ 【改进建议代码示例】

+++ b/src/dbus/applicationservice.cpp
@@ -15,6 +15,10 @@
 #include <QFileInfo>
 #include <QLoggingCategory>
 #include <QDir>
+#include <fcntl.h>
+#include <unistd.h>
+#include <cerrno>
+#include <cstring>
 
 Q_LOGGING_CATEGORY(appServiceLog, "deepin.app.service", QtWarningMsg)
 
@@ -961,14 +965,13 @@ bool ApplicationService::autostartCheck() const noexcept
 
 bool ApplicationService::saveAutostartEntry(const QString &fileName, const DesktopEntry &entry) noexcept
 {
-    const QFileInfo autostartFileInfo{fileName};
-    if (autostartFileInfo.isSymLink()) {
-        qWarning() << "refuse to overwrite symlink autostart file:" << fileName;
+    // 使用 O_NOFOLLOW 原子化防止 TOCTOU 竞态条件导致的符号链接攻击
+    int fd = ::open(fileName.toLocal8Bit().constData(), O_WRONLY | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0644);
+    if (fd == -1) {
+        qWarning() << "open file" << fileName << "failed:" << strerror(errno);
         return false;
     }
 
-    QFile autostartFile{fileName};
-    if (!autostartFile.open(QFile::WriteOnly | QFile::Text | QFile::Truncate)) {
-        qWarning() << "open file" << fileName << "failed:" << autostartFile.error();
+    if (!autostartFile.open(fd, QIODevice::WriteOnly | QIODevice::Text)) {
+        qWarning() << "bind fd to QFile failed:" << fileName;
+        ::close(fd);
         return false;
     }

本服务为session级别的服务,无法提权去覆盖系统级别的文件,为误报。

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: BLumia, yixinshark

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@yixinshark

Copy link
Copy Markdown
Contributor Author

/test all

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants