Conversation
Reviewer's GuideRefactors storage and rV integration around a new per-library rule file and rV.db, adds richer metadata capture and ComicInfo generation, reworks the updater to use tags and docs-hosted notes, and updates GUI/CLI tooling and website parsers accordingly for v2.8.0. Sequence diagram for RV scan and episode syncsequenceDiagram
actor User
participant rvTool as rvTool
participant RVManager as RVManager
participant RvThread as RvThread
participant Handler as Handler
participant SqlrV as SqlrV
participant FS as FileSystem
participant CgsRuleMgr as CgsRuleMgr
participant SpiderGUI as SpiderGUI
User->>rvTool: click Scan Local
rvTool->>RVManager: start_scan(show_progress=True parent_widget=self pos=infobar_pos)
RVManager->>RVManager: check existing scan_thread
RVManager->>RvThread: create(gui, show_progress=True)
RVManager->>RvThread: connect signals
RVManager->>RvThread: start()
RvThread->>RVManager: scan_progress("backend scaning local...")
RVManager->>User: InfoBar.info("backend scaning local...")
RvThread->>Handler: scan(conf, init=False)
activate Handler
Handler->>CgsRuleMgr: get_download_handle(conf)
CgsRuleMgr-->>Handler: downloaded_handle
Handler->>Handler: _scan_filesystem(conf.sv_path)
Handler->>FS: iterate sv_path
FS-->>Handler: book and episode paths
Handler->>Handler: strategy.should_scan_as_episode(item)
Handler->>Handler: strategy.get_episode_name(item)
Handler-->>RvThread: fs_episodes set
RvThread->>SqlrV: __enter__()
activate SqlrV
SqlrV->>SqlrV: connect(), create tables if needed
RvThread->>SqlrV: reset_exist() when init True (optional)
RvThread->>SqlrV: sync_episodes(fs_episodes)
SqlrV->>SqlrV: get_episodes()
SqlrV->>SqlrV: diff FS vs DB
SqlrV->>SqlrV: batch_write_episodes(updates)
SqlrV-->>RvThread: done
RvThread->>SqlrV: __exit__()
SqlrV->>SqlrV: close()
deactivate SqlrV
Handler-->>RvThread: total=len(fs_episodes)
deactivate Handler
RvThread->>RVManager: scan_completed(total)
RVManager->>SpiderGUI: reset gui.bsm=None
RVManager->>User: InfoBar.success or InfoBar.warning "scaned: total books/epsiodes"
RvThread-->>RVManager: thread finished
Class diagram for new rV scanning and storage rule systemclassDiagram
class Handler {
+ero: int|None
+strategy: ScanStrategy
+__init__(ero=None)
+sql() SqlrV
+scan(_conf, init: bool) int
+_scan_filesystem(sv_path: pathlib_Path) set
+_scan_normal_comics(sv_path: pathlib_Path) set
+_scan_ero_books(sv_path: pathlib_Path) set
+show_max() Dict~str,BookShow~
+delete_record(bn: str)
}
class ScanStrategy {
<<abstract>>
+should_scan_as_episode(item: pathlib_Path) bool
+get_episode_name(item: pathlib_Path) str
}
class FolderStrategy {
+should_scan_as_episode(item: pathlib_Path) bool
+get_episode_name(item: pathlib_Path) str
}
class CbzStrategy {
+should_scan_as_episode(item: pathlib_Path) bool
+get_episode_name(item: pathlib_Path) str
}
class ScanStrategyFactory {
+_strategies: dict
+create(downloaded_handle: str) ScanStrategy
}
class BookShow {
+name: str
+show_max: str
+dl_max: str
+show() str
}
class SqlRecorder {
+init_flag: bool
+__init__()
+check_dupe(taskid: str) bool
+batch_check_dupe(taskids: list) list
+add(taskid: str)
+close()
}
class SqlrV {
+init_flag: bool
+db: pathlib_Path
+written_meta: set
+meta_tb: str
+eps_tb: str
+ero: int|None
+__init__(ero: int|None)
+__enter__() SqlrV
+__exit__(exc_type, exc_val, exc_tb) bool
+connect() SqlrV
+table_exists() bool
+create()
+write_meta(**sql_kw)
+write_episode(book: str, ep: str, exist: int)
+batch_write_episodes(episodes: list)
+get_episodes(book: str) list
+delete_episodes(book: str)
+reset_exist()
+sync_episodes(fs_episodes: set)
+close()
}
class CgsRuleMgr {
+RULE_FILENAME: str
+_exist_flags: dict
+_cached_rules: dict
+_get_rule_file(sv_path: Path) Path
+exists(sv_path: Path) bool
+create(sv_path: Path, conf_dh: str) bool
+get_download_handle(_conf) str
+validate(sv_path: Path, new_handle: str) Tuple~bool,str~
}
class RVManager {
+pos: InfoBarPosition
+gui
+scan_thread: RvThread
+__init__(gui)
+start_scan(show_progress: bool)
+_show_scan_progress(message: str)
+_on_scan_completed(total: int)
+stop_scan()
}
class RvThread {
+scan_completed: pyqtSignal
+scan_progress: pyqtSignal
+gui
+show_progress: bool
+__init__(gui, show_progress: bool)
+run()
}
class SpiderGUI {
+rv_tools: Handler
+rv_mgr: RVManager
+say_show_max()
}
class rvTool {
+gui
+infobar_pos: InfoBarPosition
+show_max()
+rv_scan()
+run()
}
Handler --> ScanStrategy : uses
Handler --> SqlrV : uses
Handler --> BookShow : builds
ScanStrategy <|-- FolderStrategy
ScanStrategy <|-- CbzStrategy
ScanStrategyFactory --> ScanStrategy : creates
ScanStrategyFactory --> FolderStrategy
ScanStrategyFactory --> CbzStrategy
SpiderGUI --> Handler : rv_tools
SpiderGUI --> RVManager : rv_mgr
rvTool --> Handler : gui.rv_tools
rvTool --> RVManager : gui.rv_mgr
RVManager --> RvThread : creates
RvThread --> Handler : gui.rv_tools.scan
RvThread --> SqlrV : via Handler.sql
SqlrV --> CgsRuleMgr : uses get_download_handle via Handler
CgsRuleMgr ..> Handler : configures scan rules
SqlRecorder <.. SpiderGUI : used for dedupe
SqlRecorder <.. BaseComicSpider : record downloads
Class diagram for updated metadata and ComicInfo generationclassDiagram
class MetaMixin {
}
class BookInfo {
+name: str
+artist: str|None
+source: str
+preview_url: str
+public_date: str|None
+tags: list
+pages: int|str|None
+btype: str|None
+to_sql() dict
+clip_info()
+to_tasks_obj() TasksObj
+id_and_md5() tuple
}
class Episode {
+from_book: BookInfo
+id: str
+idx: int
+url: str
+name: str|None
+pages: int|str|None
+id_and_md5() tuple
}
class TasksObj {
+taskid: str
+title: str
+tasks_count: int
+title_url: str|None
+episode_name: str|None
+display_title: str
}
class ComicInfo {
+file: str
+is_ep: bool
+title: str
+series: str
+number: str|None
+pages: int|None
+preview_url: str
+artist: str|None
+tags: list
+btype: list|None
+source: str
+public_date: str|None
+year: str|None
+month: str|None
+day: str|None
+language_iso: str
+_extra_fields: dict
+__init__(info: BookInfo|Episode)
+_extract_series_name(name: str) str
+_extract_number_from_episode_name(episode_name: str) str|None
+_parse_date()
+_parse_language() str
+add(tag: str, value: str|None) ComicInfo
+content: str
+fin_callback(_p: pathlib_Path)
}
class Blank {
}
class ComicspiderItem {
+get_group_infos(resp_meta: dict) dict
}
class SpiderImagesPipeline {
+file_path(request, response, info, item) str
+file_folder(basepath, section, spider, title, item) str
+page_naming(taskid, page, info) str
}
class create_cbz_func {
+create_cbz(src: pathlib_Path, is_ep: bool)
}
BookInfo <|-- Episode : from_book
MetaMixin <|-- ComicInfo
MetaMixin <|-- Blank
ComicInfo --> BookInfo : init with
ComicInfo --> Episode : init with
BookInfo --> TasksObj : to_tasks_obj
TasksObj --> ComicInfo : meta_info in TasksObj
create_cbz_func ..> ComicInfo : used in fin_callback
SpiderImagesPipeline --> ComicspiderItem : uses group infos
SpiderImagesPipeline --> create_cbz_func : calls
SpiderImagesPipeline --> ComicInfo : sv_meta_in/fin_callback
ComicspiderItem --> BookInfo : title
ComicspiderItem --> Episode : section may be None
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 8 security issues, 7 other issues, and left some high level feedback:
Security issues:
- Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option. (link)
- Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option. (link)
- Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option. (link)
- Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option. (link)
- Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option. (link)
- Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option. (link)
- Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option. (link)
- Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option. (link)
General comments:
- The
CgsRuleMgr.createimplementation looks fragile: it monkey‑patchescreateon first call and caches_exist_flagsusingstr(sv_path)whileexists()caches byPath, which means laterexists()calls for the same path won’t see theTrueflag; consider removing the self‑reassignment and normalizing the key type (e.g., alwaysPath.resolve()or alwaysstr) consistently acrossexists/create/validate/get_download_handle. - In
CgsRuleMgr.validate, when the.cgsRule.jsonfile cannot be read you silently return(True, ""), which hides corruption or partial writes; it would be safer to either surface a warning/InfoBar or force re‑creation of the rule file so users understand why validation is effectively bypassed. - The
SqlrV.get_metamethod currently contains only a triple‑quoted string with example code and returnsNoneat runtime; if it’s meant to be a stub, consider either implementing it or clearly marking itpasswith a docstring/comment so it doesn’t look like a partially commented‑out implementation.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `CgsRuleMgr.create` implementation looks fragile: it monkey‑patches `create` on first call and caches `_exist_flags` using `str(sv_path)` while `exists()` caches by `Path`, which means later `exists()` calls for the same path won’t see the `True` flag; consider removing the self‑reassignment and normalizing the key type (e.g., always `Path.resolve()` or always `str`) consistently across `exists/create/validate/get_download_handle`.
- In `CgsRuleMgr.validate`, when the `.cgsRule.json` file cannot be read you silently return `(True, "")`, which hides corruption or partial writes; it would be safer to either surface a warning/InfoBar or force re‑creation of the rule file so users understand why validation is effectively bypassed.
- The `SqlrV.get_meta` method currently contains only a triple‑quoted string with example code and returns `None` at runtime; if it’s meant to be a stub, consider either implementing it or clearly marking it `pass` with a docstring/comment so it doesn’t look like a partially commented‑out implementation.
## Individual Comments
### Comment 1
<location> `utils/config/rule.py:18-23` </location>
<code_context>
+ return sv_path / cls.RULE_FILENAME
+
+ @classmethod
+ def exists(cls, sv_path: Path) -> bool:
+ if sv_path in cls._exist_flags:
+ return cls._exist_flags[sv_path]
+ result = cls._get_rule_file(sv_path).exists()
+ cls._exist_flags[sv_path] = result
+ return result
+
+ @classmethod
</code_context>
<issue_to_address>
**issue (bug_risk):** Inconsistent cache keys for `sv_path` can cause `exists()` and rule caching to misbehave
`exists()` uses the `Path` object as the cache key for `_exist_flags`, while `create()` and `get_download_handle()`/`validate()` use `str(sv_path)` for `_cached_rules`. This desynchronizes the caches, so `exists()` can return `False` even after a rule is created and cached, or vice versa. Please standardize on a single key type for both caches (either `Path` or `str(sv_path)`).
</issue_to_address>
### Comment 2
<location> `utils/config/rule.py:26-35` </location>
<code_context>
+ return result
+
+ @classmethod
+ def create(cls, sv_path: Path, conf_dh) -> bool:
+ if cls.exists(sv_path):
+ cls.create = lambda _, __: None
+ return False
+ try:
+ rule_data = {"downloaded_handle": conf_dh}
+ with open(cls._get_rule_file(sv_path), 'w', encoding='utf-8') as f:
+ json.dump(rule_data, f, ensure_ascii=False, indent=2)
+ key = str(sv_path)
+ cls._exist_flags[key] = True
+ cls._cached_rules[key] = rule_data
+ return True
+ except Exception:
+ return False
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Overwriting `CgsRuleMgr.create` at runtime is fragile and can break future calls
Rebinding `cls.create` to a no-op inside `create()` mutates the class globally, so any later `CgsRuleMgr.create()` call (even for a different `sv_path`) will silently do nothing. Instead, just return `False` when the rule file exists and rely on `exists()` and its cache for the fast path, without changing the method itself.
</issue_to_address>
### Comment 3
<location> `GUI/manager/rv.py:39-40` </location>
<code_context>
+ self.gui.log.info(f"scaned: {total} episodes scanned")
+ self.gui.bsm = None
+ parent = show_kws.get("parent_widget", self.gui.textBrowser)
+ info_what = InfoBar.success if total else InfoBar.warning
+ info_what(title='', content=f'scaned: {total} books/epsiodes',
+ position=show_kws['pos'], duration=3000, parent=parent)
+
</code_context>
<issue_to_address>
**nitpick (typo):** User-facing scan completion message has typos and might confuse users
`scaned` and `epsiodes` are misspelled in this user-visible message. Please correct the wording (for example: `f'Scanned: {total} books/episodes'`) and ensure it’s localized if needed.
Suggested implementation:
```python
def _on_scan_completed(self, total: int, **show_kws):
# Log a clear, correctly spelled message for developers
self.gui.log.info(f"Scanned: {total} episodes")
self.gui.bsm = None
parent = show_kws.get("parent_widget", self.gui.textBrowser)
```
In the same file (`GUI/manager/rv.py`), update the user-facing `InfoBar` completion message that currently uses the typoed string:
- Replace any `content=f'scaned: {total} books/epsiodes'` with a correctly spelled and clearer variant, for example:
- `content=self.tr(f'Scanned: {total} books/episodes')` if you already use Qt localization (`self.tr`) elsewhere, or
- `content=f'Scanned: {total} books/episodes'` if no localization mechanism is in place.
Also check for any other occurrences of `scaned` or `epsiodes` in this file (or related GUI scan code) and correct them similarly.
</issue_to_address>
### Comment 4
<location> `docs/config/index.md:75` </location>
<code_context>
-::: tip 了解其他热门元数据标准的可参与开发,或 [提需求(详细描述)](https://github.com/jasoneri/ComicGUISpider/issues/new?template=feature-request.yml)
+1. 为区分 🔞 存储路径,要想无感使用 Komga 等框架只能分开存储路径下载
+2. 同一存储目录混放不同后处理时,会导致 rV/CGS 扫描异常
+3. 注意不同获取方式的元数据字段可能不同,搜索接口的一般没有书页接口(例如读剪贴板/车号)的全
:::
</code_context>
<issue_to_address>
**issue (typo):** 句尾“的全”语义不完整,建议补全表述。
结尾“的全”读起来像是少了个词,建议改成类似“没有书页接口(例如读剪贴板/车号)的那么全”,语义会更完整自然。
Suggested implementation:
```
1. 为区分 🔞 存储路径,要想无感使用 Komga 等框架只能分开存储路径下载
2. 同一存储目录混放不同后处理时,会导致 rV/CGS 扫描异常
3. 注意不同获取方式的元数据字段可能不同,搜索接口的一般没有书页接口(例如读剪贴板/车号)的那么全
```
如果该段落在文档中还有英文版本或重复描述的位置,请同步将对应位置的句尾从“的全”改为“的那么全”,保持多语言文档的一致性。
</issue_to_address>
### Comment 5
<location> `README.md:99` </location>
<code_context>
+ </tr>
+ <tr>
+ <td>rV, 自用<br>全面无感适配 CGS<br><s>CGS 为它服务</s></td>
+ <td>komga/ComicRack系<br>需后处理设<code>.czb</code></td>
+ <td>PicView<br>图片管理器, 但用来操作子目录图片<br>或是<code>.cbz</code>都是不错选择</td>
+ </tr>
</code_context>
<issue_to_address>
**issue (typo):** 疑似将“.cbz”误写为“.czb”。
上文关于后处理和阅读器始终用的是 `.cbz`,这里写成 `.czb` 应该是笔误。建议改为 `.cbz`,保持扩展名一致,避免用户配置出错。
```suggestion
<td>komga/ComicRack系<br>需后处理设<code>.cbz</code></td>
```
</issue_to_address>
### Comment 6
<location> `docs/index.md:97-99` </location>
<code_context>
+ </tr>
+ <tr>
+ <td>rV, 自用<br>全面无感适配 CGS<br><s>CGS 为它服务</s></td>
+ <td>komga/ComicRack系<br>需后处理设<code>.czb</code></td>
+ <td>PicView<br>图片管理器, 但用来操作子目录图片<br>或是<code>.cbz</code>都是不错选择</td>
+ </tr>
</code_context>
<issue_to_address>
**issue (typo):** 这里的“.czb”扩展名与其他文档不一致,可能是“.cbz”的笔误。
与 README 中相同位置的说明相比,这里应当也是指 `.cbz` 格式。如无特殊含义,请改为 `.cbz` 以保持一致。
```suggestion
<td>rV, 自用<br>全面无感适配 CGS<br><s>CGS 为它服务</s></td>
<td>komga/ComicRack系<br>需后处理设<code>.cbz</code></td>
<td>PicView<br>图片管理器, 但用来操作子目录图片<br>或是<code>.cbz</code>都是不错选择</td>
```
</issue_to_address>
### Comment 7
<location> `docs/dev/link.md:20` </location>
<code_context>
+## 其他
+
+- [🎅LDC 投喂](https://credit.linux.do/paying/online?token=26af47ee90b1192086095d107dc9bc1ca4137bd12496fefaf22efadcc349a98a)
+- [学英语浏览器插件](Ries.ai?c=Jzva)
</code_context>
<issue_to_address>
**issue (bug_risk):** Ries 链接缺少协议,可能会被当作相对路径处理。
如果是外部站点,请改成完整地址,例如 `https://Ries.ai?c=Jzva`,以避免在不同渲染环境中被错误解析为相对链接。
</issue_to_address>
### Comment 8
<location> `utils/sql/__init__.py:127` </location>
<code_context>
self.cursor.execute(meta_tb_sql)
</code_context>
<issue_to_address>
**security (python.sqlalchemy.security.sqlalchemy-execute-raw-query):** Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
*Source: opengrep*
</issue_to_address>
### Comment 9
<location> `utils/sql/__init__.py:128` </location>
<code_context>
self.cursor.execute(eps_tb_sql)
</code_context>
<issue_to_address>
**security (python.sqlalchemy.security.sqlalchemy-execute-raw-query):** Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
*Source: opengrep*
</issue_to_address>
### Comment 10
<location> `utils/sql/__init__.py:145-148` </location>
<code_context>
self.cursor.execute(sql, (
book_md5, book_name,
sql_kw.get('artist'), sql_kw.get('source'), sql_kw.get('preview_url'), sql_kw.get('public_date'),
tags_str, sql_kw.get('pages'), sql_kw.get('btype'), self.ero))
</code_context>
<issue_to_address>
**security (python.sqlalchemy.security.sqlalchemy-execute-raw-query):** Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
*Source: opengrep*
</issue_to_address>
### Comment 11
<location> `utils/sql/__init__.py:159` </location>
<code_context>
self.cursor.execute(sql, (set_author_ahead(book), ep or '', exist, None, self.ero))
</code_context>
<issue_to_address>
**security (python.sqlalchemy.security.sqlalchemy-execute-raw-query):** Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
*Source: opengrep*
</issue_to_address>
### Comment 12
<location> `utils/sql/__init__.py:204` </location>
<code_context>
self.cursor.execute(sql, params)
</code_context>
<issue_to_address>
**security (python.sqlalchemy.security.sqlalchemy-execute-raw-query):** Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
*Source: opengrep*
</issue_to_address>
### Comment 13
<location> `utils/sql/__init__.py:209` </location>
<code_context>
self.cursor.execute(sql, (book,))
</code_context>
<issue_to_address>
**security (python.sqlalchemy.security.sqlalchemy-execute-raw-query):** Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
*Source: opengrep*
</issue_to_address>
### Comment 14
<location> `utils/sql/__init__.py:215` </location>
<code_context>
self.cursor.execute(sql, (self.ero,))
</code_context>
<issue_to_address>
**security (python.sqlalchemy.security.sqlalchemy-execute-raw-query):** Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
*Source: opengrep*
</issue_to_address>
### Comment 15
<location> `utils/sql/__init__.py:218` </location>
<code_context>
self.cursor.execute(sql)
</code_context>
<issue_to_address>
**security (python.sqlalchemy.security.sqlalchemy-execute-raw-query):** Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
*Source: opengrep*
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| info_what = InfoBar.success if total else InfoBar.warning | ||
| info_what(title='', content=f'scaned: {total} books/epsiodes', |
There was a problem hiding this comment.
nitpick (typo): User-facing scan completion message has typos and might confuse users
scaned and epsiodes are misspelled in this user-visible message. Please correct the wording (for example: f'Scanned: {total} books/episodes') and ensure it’s localized if needed.
Suggested implementation:
def _on_scan_completed(self, total: int, **show_kws):
# Log a clear, correctly spelled message for developers
self.gui.log.info(f"Scanned: {total} episodes")
self.gui.bsm = None
parent = show_kws.get("parent_widget", self.gui.textBrowser)In the same file (GUI/manager/rv.py), update the user-facing InfoBar completion message that currently uses the typoed string:
- Replace any
content=f'scaned: {total} books/epsiodes'with a correctly spelled and clearer variant, for example:content=self.tr(f'Scanned: {total} books/episodes')if you already use Qt localization (self.tr) elsewhere, orcontent=f'Scanned: {total} books/episodes'if no localization mechanism is in place.
Also check for any other occurrences ofscanedorepsiodesin this file (or related GUI scan code) and correct them similarly.
Deploying comicguispider-docs with
|
| Latest commit: |
6fb5f15
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://58d4e258.comicguispider-docs.pages.dev |
| Branch Preview URL: | https://2-7-dev.comicguispider-docs.pages.dev |
| UNIQUE(`book`, `ep`) | ||
| );''' | ||
|
|
||
| self.cursor.execute(meta_tb_sql) |
There was a problem hiding this comment.
security (python.sqlalchemy.security.sqlalchemy-execute-raw-query): Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
Source: opengrep
| );''' | ||
|
|
||
| self.cursor.execute(meta_tb_sql) | ||
| self.cursor.execute(eps_tb_sql) |
There was a problem hiding this comment.
security (python.sqlalchemy.security.sqlalchemy-execute-raw-query): Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
Source: opengrep
| self.cursor.execute(sql, ( | ||
| book_md5, book_name, | ||
| sql_kw.get('artist'), sql_kw.get('source'), sql_kw.get('preview_url'), sql_kw.get('public_date'), | ||
| tags_str, sql_kw.get('pages'), sql_kw.get('btype'), self.ero)) |
There was a problem hiding this comment.
security (python.sqlalchemy.security.sqlalchemy-execute-raw-query): Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
Source: opengrep
| exist = excluded.exist, | ||
| rv_handle = excluded.rv_handle, | ||
| ero = excluded.ero;''' | ||
| self.cursor.execute(sql, (set_author_ahead(book), ep or '', exist, None, self.ero)) |
There was a problem hiding this comment.
security (python.sqlalchemy.security.sqlalchemy-execute-raw-query): Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
Source: opengrep
| FROM {self.eps_tb} {where_clause} | ||
| ORDER BY book, ep;''' | ||
|
|
||
| self.cursor.execute(sql, params) |
There was a problem hiding this comment.
security (python.sqlalchemy.security.sqlalchemy-execute-raw-query): Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
Source: opengrep
|
|
||
| def delete_episodes(self, book: str): | ||
| sql = f'''DELETE FROM {self.eps_tb} WHERE book = ?;''' | ||
| self.cursor.execute(sql, (book,)) |
There was a problem hiding this comment.
security (python.sqlalchemy.security.sqlalchemy-execute-raw-query): Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
Source: opengrep
| def reset_exist(self): | ||
| if self.ero is not None: | ||
| sql = f'''UPDATE {self.eps_tb} SET exist = 0 WHERE ero = ?;''' | ||
| self.cursor.execute(sql, (self.ero,)) |
There was a problem hiding this comment.
security (python.sqlalchemy.security.sqlalchemy-execute-raw-query): Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
Source: opengrep
| self.cursor.execute(sql, (self.ero,)) | ||
| else: | ||
| sql = f'''UPDATE {self.eps_tb} SET exist = 0;''' | ||
| self.cursor.execute(sql) |
There was a problem hiding this comment.
security (python.sqlalchemy.security.sqlalchemy-execute-raw-query): Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
Source: opengrep
Description
refactor: sv directory structure
upd: Complete metainfo
fix(try): reboot lock
Related Issues
Checklist:
Summary by Sourcery
Introduce rV-aware storage and metadata tracking with a new scanning pipeline, update mechanisms, and documentation for the v2.8.0 release.
New Features:
Bug Fixes:
Enhancements:
Documentation:
Chores: