-
Notifications
You must be signed in to change notification settings - Fork 0
fix: restore CI self-check gates #423
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bf4c8e7
61d517b
6ae127f
b9c03ca
68099b6
b00c2d1
fd93386
c9fa5fd
3d6e099
a58a5db
8be7da1
a19ba1d
66be058
8f680aa
7e51488
1273c83
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -195,6 +195,58 @@ jobs: | |||||
| with: | ||||||
| fail-on-severity: low | ||||||
|
|
||||||
| # Fast PR fuzz smoke. Sustained blocking and scheduled lanes live in their | ||||||
| # dedicated workflows; keep this deterministic and bounded at 10 seconds. | ||||||
| fuzz-smoke: | ||||||
| name: fuzz smoke | ||||||
| needs: detect | ||||||
| if: needs.detect.outputs.has_rust == 'true' | ||||||
| runs-on: ubuntu-latest | ||||||
| timeout-minutes: 8 | ||||||
| permissions: | ||||||
| contents: read | ||||||
| env: | ||||||
| RUSTFLAGS: "" | ||||||
| RUSTUP_TOOLCHAIN: nightly | ||||||
| steps: | ||||||
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | ||||||
| - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # nightly for fuzz | ||||||
| with: | ||||||
| toolchain: nightly | ||||||
| components: rust-src | ||||||
|
Comment on lines
+209
to
+216
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== workflow context =="
if [ -f .github/workflows/ci.yml ]; then
sed -n '1,280p' .github/workflows/ci.yml | cat -n
fi
echo "== search for rust nightly / cargo-fuzz / RUSTUP_TOOLCHAIN =="
rg -n "RUSTUP_TOOLCHAIN|rust-toolchain|cargo-fuzz|nightly|nightly|AddressСанizer|asan|instrument| fuzz" .github . 2>/dev/null | head -200 || trueRepository: KooshaPari/SessionLedger Length of output: 34427 🌐 Web query:
💡 Result: The nightly release channel in Rust is updated daily [1]. Because nightly builds are experimental, they may occasionally be unavailable or lack certain non-default components (such as clippy or rustfmt) [1][2]. When rustup updates the nightly channel, it attempts to find the most recent version that contains all your currently installed components [3]. If the latest nightly is missing a component, rustup will automatically search for an older, viable nightly release that satisfies your requirements [3]. To force an update to the absolute latest nightly regardless of missing components, you can use the --force flag with rustup toolchain install [1][2]. To pin a project to a specific, stable version of a nightly toolchain, you should use a rust-toolchain.toml file in your project root [4]. This file ensures that everyone working on the project uses the same toolchain [4]: [toolchain] channel = "nightly-2022-01-01" When using the dtolnay/rust-toolchain GitHub Action, you can specify the nightly channel either via the action revision (e.g., uses: dtolnay/rust-toolchain@nightly) or by using the Citations:
🌐 Web query:
💡 Result: As of August 2026, AddressSanitizer (ASan) in Rust continues to be primarily supported via the nightly channel, though work has been ongoing to stabilize it [1][2]. Key points regarding AddressSanitizer and the nightly channel: Usage: To enable AddressSanitizer, you typically use the nightly toolchain and the -Z sanitizer=address compiler flag [1][3]. Because this is an unstable feature, it generally requires -Z build-std to rebuild the standard library with instrumentation, ensuring that both your code and the standard library are compatible [1][4]. Target Requirements: You must specify a target triple when using these flags to avoid instrumenting procedural macros or build scripts, which can cause build failures [1]. Ongoing Changes: There have been significant efforts to stabilize AddressSanitizer and LeakSanitizer [2]. As these features transition toward stability, there has been a shift away from exclusive reliance on the -Zsanitizer flag toward standard codegen options, although -Zunstable-options is often still required during the transition [2]. Specialized Targets: For specific platforms like x86_64-unknown-linux-gnu, there have been dedicated "asan" targets (e.g., x86_64-unknown-linux-gnuasan) intended to allow the use of pre-instrumented standard libraries, reducing the need for build-std [5]. Caveats: ABI Mismatches: Mixing instrumented and non-instrumented code can lead to ABI mismatch errors [4]. Procedural Macros: It is advised to avoid applying sanitizer flags to procedural macros or build scripts by explicitly passing the --target flag to Cargo [1]. For the most up-to-date instructions for your specific environment, refer to the Rust Unstable Book section on compiler flags for sanitizers [1]. Citations:
🌐 Web query:
💡 Result: Using the nightly channel with tools like cargo-fuzz and managing reproducible builds involves several distinct mechanisms in the Rust ecosystem. Nightly Toolchain and Reproducibility The nightly channel provides the latest, experimental compiler features but is not guaranteed to be stable or reproducible by default [1]. While there is ongoing, active development toward making rustc builds more deterministic—including discussions around new flags like --reproducible to manage diagnostic and binary output [2][3]—achieving bit-for-bit reproducibility remains a significant challenge [4][5]. Factors such as the presence of standard library source code on the host can affect build outputs, sometimes resulting in different hash values for the same source code [6]. For high-stakes reproducible needs, some users find that building the compiler from source (bootstrapping) can yield more deterministic results than relying on pre-built nightly binaries [7]. You can pin to a specific nightly release using rustup to ensure consistency across environments [8][1]. Cargo Fuzz and Sanitizers Cargo-fuzz is a toolchain-dependent utility that typically requires the nightly compiler because it relies on unstable command-line flags and LLVM sanitizer support [9][10][11]. 1. Requirements: By default, the standard fuzzing workflow requires a nightly compiler to access features like address sanitization [10]. 2. Sanitizers: Sanitizers (e.g., AddressSanitizer, MemorySanitizer) are enabled via compiler flags in the nightly channel [10][12]. 3. Stable Support: While the default, sanitizer-backed workflow is nightly-only, cargo-fuzz supports a reduced mode using --sanitizer none that can operate on stable Rust, though this mode does not provide sanitizer-based findings [13]. To set up your environment, use rustup to manage the toolchain [10]: $ rustup install nightly $ rustup default nightly If your project requires a specific nightly version for reproducibility, you can create a rust-toolchain.toml file in your project root to pin it [8][1]: [toolchain] channel = "nightly-2026-08-07" Top results: [8], [6], [1], [10], [13], [3], [5] Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== rust toolchain pin files =="
for f in rust-toolchain rust-toolchain.toml docs/ops/rustc-toolchain-pin.md rustc-toolchain-pin.json; do
if [ -f "$f" ]; then
echo "--- $f"
cat -n "$f"
fi
done
echo "== selfcheck script relevant sections =="
sed -n '45,185p' scripts/rustc-toolchain-check.ps1 | cat -n
python3 - <<'PY'
from pathlib import Path
import re, json
for p in ['rust-toolchain.toml', 'rust-toolchain']:
s = p.read_text(errors='ignore') if p.exists() else ''
print(f'FILE {p}:')
print(s[:800] if s else '(missing)')
m = re.search(r'(?:^\[toolchain\]\s*$|[\s\n])channel\s*=\s*"([^"]+)"', s, re.M) or re.search(r'(?:^|\s)(nightly-?[^\s,;}]+)', s)
print('derived channel:', m.group(1) if m else 'not found')
for p in ['docs/ops/rustc-toolchain-pin.md', 'rustc-toolchain-pin.json']:
if p.exists():
print(f'--- {p}')
if p.suffix == '.json':
print(json.dumps(json.loads(p.read_text()), indent=2))
else:
print(p.read_text())
PYRepository: KooshaPari/SessionLedger Length of output: 10495 🌐 Web query:
💡 Result: The dtolnay/rust-toolchain GitHub Action is designed to install a specified Rust toolchain using rustup [1][2]. It does not natively use the RUSTUP_TOOLCHAIN environment variable as an input; instead, it relies on its own toolchain input parameter or the action's revision ( Citations:
Pin the nightly compiler used by the fuzz gate.
🧰 Tools🪛 zizmor (1.29.0)[warning] 212-212: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false (artipacked) [info] 213-213: action functionality is already included by the runner (superfluous-actions): use (superfluous-actions) 🤖 Prompt for AI Agents |
||||||
| targets: x86_64-unknown-linux-gnu | ||||||
| - uses: taiki-e/install-action@e28ac56891501ddb0600608470dbe94544964ed4 # cargo-fuzz | ||||||
| with: | ||||||
| tool: cargo-fuzz | ||||||
| - name: fuzz smoke OKF parse and roundtrip (10s) | ||||||
| run: cargo +nightly fuzz run okf_roundtrip --sanitizer address --target x86_64-unknown-linux-gnu -- -max_total_time=10 | ||||||
| - name: fuzz smoke JSONL ingest parse (10s) | ||||||
| run: cargo +nightly fuzz run jsonl_ingest --sanitizer address --target x86_64-unknown-linux-gnu -- -max_total_time=10 | ||||||
|
|
||||||
| # Blocking C04 L40 rootless-only OCI runner matrix scaffold. The full gate | ||||||
| # lives in rootless-matrix.yml; this policy job keeps ci.yml cross-referenced | ||||||
| # and executes the same hermetic SelfCheck. | ||||||
| rootless-matrix-policy: | ||||||
| name: rootless-only matrix policy | ||||||
| runs-on: ubuntu-latest | ||||||
| steps: | ||||||
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | ||||||
| - name: assert rootless-only OCI runner scaffold anchors | ||||||
| shell: pwsh | ||||||
| run: ./scripts/rootless-matrix-check.ps1 -SelfCheck | ||||||
|
|
||||||
| # Blocking C04 L40 rootless/no-net scaffold. The full gate lives in | ||||||
| # rootless-nonet.yml; this policy job keeps ci.yml cross-referenced and | ||||||
| # executes the same hermetic SelfCheck on every pull request. | ||||||
| rootless-nonet-policy: | ||||||
| name: rootless/no-net policy | ||||||
| runs-on: ubuntu-latest | ||||||
| steps: | ||||||
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | ||||||
| - name: assert rootless/no-net scaffold anchors | ||||||
| shell: pwsh | ||||||
| run: ./scripts/rootless-nonet-check.ps1 -SelfCheck | ||||||
|
|
||||||
| # Platform signing-readiness policy (C04 L32 / C11 L112). The hard blocking | ||||||
| # gate lives in signing-hard.yml (runs on pull_request); this job runs the | ||||||
| # same SelfCheck here so the signing posture is asserted on every PR and | ||||||
|
|
@@ -210,6 +262,19 @@ jobs: | |||||
| shell: pwsh | ||||||
| run: ./scripts/signing-readiness-check.ps1 -SelfCheck | ||||||
|
|
||||||
| # Eval reproducibility manifest contract (C08 L79). Keep the workflow | ||||||
| # anchor next to the hermetic SelfCheck so the Rust wrapper can verify that | ||||||
| # CI actually runs the same no-network manifest/docs check. | ||||||
| eval-reproducibility: | ||||||
| name: Eval Reproducibility SelfCheck | ||||||
| if: github.event_name == 'pull_request' | ||||||
| runs-on: ubuntu-latest | ||||||
| steps: | ||||||
| - uses: actions/checkout@v7 | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "## workflow checkout usages"
rg -n "actions/checkout@" .github/workflows/ci.yml || true
echo
echo "## git diff stat/name only"
git diff --stat
git diff --name-only
echo
echo "## relevant ci.yml lines around checkout refs"
sed -n '240,290p' .github/workflows/ci.yml
echo
echo "## all checkout refs with line numbers in workflow"
rg -n --line-number "actions/checkout@" .github/workflows/ci.ymlRepository: KooshaPari/SessionLedger Length of output: 3554 🌐 Web query:
💡 Result: As of August 7, 2026, the current major version of actions/checkout is v7 [1][2]. The most recent patch release within this series is v7.0.1, which was published on July 20, 2026 [2]. Regarding your query about the GitHub commit reference, actions/checkout v7 follows the standard versioning and usage patterns for GitHub Actions. When you use the syntax - uses: actions/checkout@v7, GitHub automatically resolves the tag v7 to the latest release within the v7 major version (currently v7.0.1) [3][4]. The v7 release introduced significant security changes, specifically restricting the checkout of fork pull request code when running in pull_request_target or workflow_run events by default [5][6]. If you need to pin to a specific commit for immutable builds, you can use the commit SHA instead of the tag, for example: - uses: actions/checkout@1044a6d # v7.0.0 You can find the specific commit SHAs for any v7.x release on the official GitHub actions/checkout releases page [1]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "## resolve actions/checkout refs"
python3 - <<'PY'
import json, subprocess
for ref in ["v7", "3d3c42e5aac5ba805825da76410c181273ba90b1"]:
print(f"--- {ref} ---")
try:
r_sub = subprocess.run(
["gh", "api", "repos/actions/checkout/git/refs/tags/" + ref],
timeout=20,
capture_output=True,
text=True,
check=True,
)
r = json.loads(r_sub.stdout)
except Exception as exc:
print("failed", repr(exc))
continue
print("ref_type", r.get("ref"))
print("target_type", r.get("object", {}).get("type"))
print("target_sha", r.get("object", {}).get("sha"))
if r.get("object", {}).get("type") == "tag":
sha = r.get("object", {}).get("sha")
try:
r_tag = subprocess.run(
["gh", "api", f"repos/actions/checkout/git/tags/{sha}"],
timeout=20,
capture_output=True,
text=True,
check=True,
)
except Exception as exc:
print("tag object lookup failed", repr(exc))
continue
print("annotated_tags", "target_sha", json.loads(r_tag.stdout).get("object", {}).get("sha"))
try:
r_ok = subprocess.run(
["gh", "api", f"repos/actions/checkout/commits/{ref}"],
timeout=20,
capture_output=True,
text=True,
check=True,
)
c = json.loads(r_ok.stdout)
print("commit_sha_exists", c.get("sha"))
except Exception as exc:
print("commit lookup failed", repr(exc))
PYRepository: KooshaPari/SessionLedger Length of output: 532 🌐 Web query:
💡 Result: The commit SHA 3d3c42e5aac5ba805825da76410c181273ba90b1 corresponds to the commit titled "prep v7.0.1 release ( Citations:
Pin this
Proposed fix- - uses: actions/checkout@v7
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7📝 Committable suggestion
Suggested change
🧰 Tools🪛 zizmor (1.29.0)[warning] 273-273: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false (artipacked) [error] 273-273: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy) (unpinned-uses) 🤖 Prompt for AI AgentsSource: Linters/SAST tools There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WARNING: Unpinned The new Reply with |
||||||
| - name: eval reproducibility SelfCheck | ||||||
| shell: pwsh | ||||||
| run: ./scripts/eval-repro-check.ps1 -SelfCheck | ||||||
|
|
||||||
| lint: | ||||||
| name: ci / lint | ||||||
| if: always() | ||||||
|
|
||||||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -146,9 +146,22 @@ fn read_sessions( | |
| Ok(vec![session]) | ||
| } | ||
|
|
||
| /// Make a session id safe to use as a filename (path separators → `_`). | ||
| fn sanitize(id: &str) -> String { | ||
| id.chars().map(|c| if matches!(c, '/' | '\\' | ':') { '_' } else { c }).collect() | ||
| /// Encode a session id as one injective, safe filename component. | ||
| /// | ||
| /// Underscores are escaped as well as path separators so an encoded separator | ||
| /// can never collide with an input that already contained the escape marker. | ||
| pub(crate) fn sanitize(id: &str) -> String { | ||
| let mut encoded = String::with_capacity(id.len()); | ||
| for character in id.chars() { | ||
| match character { | ||
| '_' => encoded.push_str("_x5f"), | ||
| '/' => encoded.push_str("_x2f"), | ||
| '\\' => encoded.push_str("_x5c"), | ||
| ':' => encoded.push_str("_x3a"), | ||
| character => encoded.push(character), | ||
| } | ||
|
Comment on lines
+155
to
+162
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: The function claims to produce a safe filename component, but characters such as Severity Level: Major
|
||
| } | ||
| encoded | ||
|
Comment on lines
+149
to
+164
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
printf 'Locate file and relevant symbols:\n'
git ls-files | rg '(^|/)etl\.rs$|Cargo\.toml|rust-toolchain\.toml' || true
printf '\nOutline etl.rs:\n'
ast-grep outline crates/sl-daemon/src/etl.rs || true
printf '\nRelevant etl.rs section:\n'
sed -n '1,220p' crates/sl-daemon/src/etl.rs
printf '\nSearch sanitize usage and session.id producers:\n'
rg -n "sanitize\(|session\.id|id:" crates/sl-daemon/src crates/sl-daemon -g '*.rs' -g '*.toml'Repository: KooshaPari/SessionLedger Length of output: 21606 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf 'Root session_ledger Session and JSONL parsing definitions:\n'
rg -n "struct Session|pub id:|read_jsonl_sessions|fn new\\(" crates/sl-daemon crates -g '*.rs' | head -200
printf '\nCandidate session domain files:\n'
git ls-files 'crates/sl-*/**/*.rs' | rg 'session|domain|bundle|model' | head -100
printf '\nInspect specific session domain files:\n'
for f in $(git ls-files 'crates/sl-*/**/*.rs' | rg 'session|domain' | head -50); do
echo "--- $f"
wc -l "$f"
done
printf '\nSl-daemon worker export write:\n'
sed -n '70,115p' crates/sl-daemon/src/worker.rs
printf '\nSl-daemon tests around pipeline out_path:\n'
sed -n '60,90p' crates/sl-daemon/tests/pipeline.rs
printf '\nSearch for session.id validation/restrictions:\n'
rg -n "session\\.id|Session::new|bundle_id|bundle.*id|valid|reject|reserved|CON|NUL|sanitize|\\.okf\\.json" crates/sl-daemon crates -g '*.rs' -g '*.toml' | head -250Repository: KooshaPari/SessionLedger Length of output: 30399 Make the ETL session-id filename encoding portable on Windows.
🤖 Prompt for AI Agents🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
rg -n -C 5 'sanitize\(|okf\.json|validate_on_disk_okf|data_dir' crates/sl-daemon/src || true
git grep -n -C 5 -E 'sanitize\(|okf\.json|validate_on_disk_okf' origin/main -- crates/sl-daemon/src || trueRepository: KooshaPari/SessionLedger Length of output: 50383 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
echo "== git branch/origin availability =="
git branch --show-current
git remote -v || true
echo
echo "== changed files =="
git diff --name-only --diff-filter=ACMRTUXB HEAD origin/main 2>/dev/null || true
echo
echo "== sanitize implementation =="
sed -n '140,165p' crates/sl-daemon/src/etl.rs
echo
echo "== transform_file path =="
sed -n '90,108p' crates/sl-daemon/src/etl.rs
echo
echo "== validate_on_disk_okf implementation =="
rg -n -C 8 'struct ValidateBundle|fn validate_on_disk_okf|run_validate|bundle_id' crates/sl-daemon/src/main.rs
echo
echo "== old sanitize references in current branch and origin/main =="
git diff --unified=0 origin/main -- crates/sl-daemon/src/etl.rs crates/sl-daemon/src/main.rs | rg -n 'sanitize|okf\.json|bundle_id|i?sub|replace' || true
git show origin/main:crates/sl-daemon/src/etl.rs 2>/dev/null | rg -n 'sanitize\(|pub(crate) fn sanitize' || trueRepository: KooshaPari/SessionLedger Length of output: 11470 Add migration semantics before changing bundle filename encoding. New bundles are written and validated with escapted filenames, but 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| #[cfg(test)] | ||
|
|
@@ -195,6 +208,32 @@ mod tests { | |
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn transform_file_keeps_colliding_ids_distinct() { | ||
| let tmp = tempfile::tempdir().expect("tempdir"); | ||
| let jsonl = tmp.path().join("collisions.jsonl"); | ||
| let sessions = ["a/b", "a_b"]; | ||
| let mut content = String::new(); | ||
| for id in sessions { | ||
| let mut session = Session::new(id, Corpus::Forge); | ||
| session.messages.push(Message::new(Role::User, "keep distinct")); | ||
| content.push_str(&serde_json::to_string(&session).expect("serialize session")); | ||
| content.push('\n'); | ||
| } | ||
| std::fs::write(&jsonl, content).expect("write fixture"); | ||
|
|
||
| let written = transform_file(&jsonl, &tmp.path().join("out"), None).expect("transform"); | ||
|
|
||
| assert_eq!(written.len(), 2); | ||
| assert_ne!(written[0], written[1]); | ||
| for (path, source_id) in written.iter().zip(sessions) { | ||
| let document: serde_json::Value = | ||
| serde_json::from_str(&std::fs::read_to_string(path).expect("read OKF")) | ||
| .expect("parse OKF"); | ||
| assert_eq!(document["source_id"], source_id); | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn transform_file_creates_missing_out_dir() { | ||
| let tmp = tempfile::tempdir().expect("tempdir"); | ||
|
|
@@ -227,7 +266,8 @@ mod tests { | |
|
|
||
| #[test] | ||
| fn sanitize_replaces_path_separators() { | ||
| assert_eq!(sanitize("a/b:c\\d"), "a_b_c_d"); | ||
| assert_eq!(sanitize("a/b:c\\d"), "a_x2fb_x3ac_x5cd"); | ||
| assert_eq!(sanitize("a_b"), "a_x5fb"); | ||
| assert_eq!(sanitize("plain-id"), "plain-id"); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -94,6 +94,20 @@ impl Tab { | |||||||||||||||||||||
| Self::ALL.iter().position(|&t| t == self).unwrap_or(0) | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /// Return the SVG icon name for this tab. | ||||||||||||||||||||||
| fn icon(&self) -> &'static str { | ||||||||||||||||||||||
| match self { | ||||||||||||||||||||||
| Self::Memory => "memory", | ||||||||||||||||||||||
| Self::Bundles => "bundles", | ||||||||||||||||||||||
| Self::History => "history", | ||||||||||||||||||||||
| Self::Unfinished => "unfinished", | ||||||||||||||||||||||
| Self::LiveFeed => "live", | ||||||||||||||||||||||
| Self::Timeline => "timeline", | ||||||||||||||||||||||
| Self::Search => "search", | ||||||||||||||||||||||
| Self::Replay => "replay", | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| fn from_index(i: usize) -> Tab { | ||||||||||||||||||||||
| Self::ALL[i % Self::ALL.len()] | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
@@ -190,6 +204,30 @@ fn build_bundles_from_sessions(sessions: &[Session]) -> Vec<ContinuationBundle> | |||||||||||||||||||||
|
|
||||||||||||||||||||||
| // `App` is a Dioxus component (mounted by name from main.rs / web entry). | ||||||||||||||||||||||
| #[allow(non_snake_case)] | ||||||||||||||||||||||
| /// Inline SVG icons for each tab. | ||||||||||||||||||||||
| const ICON_SVG_BUNDLES: &str = include_str!("../../../assets/icons/line/bundles.svg"); | ||||||||||||||||||||||
| const ICON_SVG_HISTORY: &str = include_str!("../../../assets/icons/line/history.svg"); | ||||||||||||||||||||||
| const ICON_SVG_MEMORY: &str = include_str!("../../../assets/icons/line/memory.svg"); | ||||||||||||||||||||||
| const ICON_SVG_UNFINISHED: &str = include_str!("../../../assets/icons/line/unfinished.svg"); | ||||||||||||||||||||||
| const ICON_SVG_TIMELINE: &str = include_str!("../../../assets/icons/line/timeline.svg"); | ||||||||||||||||||||||
| const ICON_SVG_LIVE: &str = include_str!("../../../assets/icons/line/live.svg"); | ||||||||||||||||||||||
| const ICON_SVG_SEARCH: &str = include_str!("../../../assets/icons/line/search.svg"); | ||||||||||||||||||||||
| const ICON_SVG_REPLAY: &str = include_str!("../../../assets/icons/line/replay.svg"); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /// Lookup table for tab icon SVGs. | ||||||||||||||||||||||
| fn icon_svg(tab_icon: &str) -> &'static str { | ||||||||||||||||||||||
| match tab_icon { | ||||||||||||||||||||||
| "bundles" => ICON_SVG_BUNDLES, | ||||||||||||||||||||||
| "history" => ICON_SVG_HISTORY, | ||||||||||||||||||||||
| "memory" => ICON_SVG_MEMORY, | ||||||||||||||||||||||
| "unfinished" => ICON_SVG_UNFINISHED, | ||||||||||||||||||||||
| "timeline" => ICON_SVG_TIMELINE, | ||||||||||||||||||||||
| "live" => ICON_SVG_LIVE, | ||||||||||||||||||||||
| "search" => ICON_SVG_SEARCH, | ||||||||||||||||||||||
| "replay" => ICON_SVG_REPLAY, | ||||||||||||||||||||||
| _ => ICON_SVG_BUNDLES, | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
Comment on lines
+207
to
+230
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win Do not hide an unmapped icon behind the Bundles fallback.
Suggested type-safe lookup-fn icon_svg(tab_icon: &str) -> &'static str {
- match tab_icon {
- "bundles" => ICON_SVG_BUNDLES,
- "history" => ICON_SVG_HISTORY,
- "memory" => ICON_SVG_MEMORY,
- "unfinished" => ICON_SVG_UNFINISHED,
- "timeline" => ICON_SVG_TIMELINE,
- "live" => ICON_SVG_LIVE,
- "search" => ICON_SVG_SEARCH,
- "replay" => ICON_SVG_REPLAY,
- _ => ICON_SVG_BUNDLES,
+fn icon_svg(tab: &Tab) -> &'static str {
+ match tab {
+ Tab::Bundles => ICON_SVG_BUNDLES,
+ Tab::History => ICON_SVG_HISTORY,
+ Tab::Memory => ICON_SVG_MEMORY,
+ Tab::Unfinished => ICON_SVG_UNFINISHED,
+ Tab::Timeline => ICON_SVG_TIMELINE,
+ Tab::LiveFeed => ICON_SVG_LIVE,
+ Tab::Search => ICON_SVG_SEARCH,
+ Tab::Replay => ICON_SVG_REPLAY,
}
}Update the caller to pass 🤖 Prompt for AI Agents |
||||||||||||||||||||||
| pub fn App() -> Element { | ||||||||||||||||||||||
| #[cfg(feature = "web")] | ||||||||||||||||||||||
| use_effect(|| { | ||||||||||||||||||||||
|
|
@@ -396,7 +434,7 @@ pub fn App() -> Element { | |||||||||||||||||||||
| Tab::Timeline => { | ||||||||||||||||||||||
| let bundles = build_bundles_from_sessions(&sessions_signal.read()); | ||||||||||||||||||||||
| rsx! { TimelineView { bundles } } | ||||||||||||||||||||||
| }, | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| Tab::Replay => rsx! { ReplayView {} }, | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
@@ -865,7 +903,10 @@ pub fn App() -> Element { | |||||||||||||||||||||
| _ => {} | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| }, | ||||||||||||||||||||||
| "{tab.label()}" | ||||||||||||||||||||||
| span { | ||||||||||||||||||||||
| dangerous_inner_html: "{icon_svg(tab.icon())}" | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| "{tab.label()}" | ||||||||||||||||||||||
|
Comment on lines
+906
to
+909
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: The injected assets are standalone SVG documents with only a Severity Level: Major
|
||||||||||||||||||||||
| span { | |
| dangerous_inner_html: "{icon_svg(tab.icon())}" | |
| } | |
| "{tab.label()}" | |
| span { | |
| "aria-hidden": "true", | |
| dangerous_inner_html: "{icon_svg(tab.icon())}" | |
| } | |
| " " | |
| "{tab.label()}" |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/sl-viewer/src/app.rs` around lines 906 - 909, Update the tab markup
around tab.icon() and tab.label() to mark the icon wrapper with
aria-hidden="true" and add explicit visual separation between the icon and
label, using a space or CSS gap without changing the existing tab label
behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: KooshaPari/SessionLedger
Length of output: 7040
🌐 Web query:
actions/checkout v7 persist-credentials default true README💡 Result:
The default value for the persist-credentials input in actions/checkout v7 is true [1]. When set to true, the action configures the Git credential helper to use the provided token or SSH key, allowing your workflow scripts to run authenticated Git commands (such as git fetch or git push) without manual configuration [2][3]. The action automatically removes these credentials during the post-job cleanup process [2][4]. In recent versions, including v7, security has been improved by persisting credentials in a separate file under the runner's temporary directory ($RUNNER_TEMP) rather than directly in the local.git/config file [5][6]. If you prefer to disable this behavior, you can explicitly set persist-credentials: false in your workflow step [2][3].
Citations:
🏁 Script executed:
Repository: KooshaPari/SessionLedger
Length of output: 4715
🌐 Web query:
github actions runner checkouts persist-credentials token $RUNNER_TEMP .git config persist-credentials true💡 Result:
In recent versions of the
actions/checkoutaction (starting with v6.0.0), the way credentials are persisted has been updated to improve security [1][2]. Instead of writing the authentication token or SSH key directly into the local repository's.git/configfile, the action now writes these credentials to a separate temporary file located in the runner's$RUNNER_TEMPdirectory [3][4][1]. Key aspects of this implementation include: - Mechanism: The action uses Git'sincludeIf.gitdirconfiguration feature [2][5]. By adding anincludeIfdirective to the local.git/config, Git is instructed to conditionally load the credentials from the file in$RUNNER_TEMPonly when operating within that specific repository's directory [2][6]. - Purpose: This approach isolates sensitive credentials from the main Git configuration and reduces the risk of accidental exposure [2][5]. - Default Behavior: Thepersist-credentialsinput defaults totrue, which enables this behavior [3][7]. If you do not want the action to configure these credentials, you can setpersist-credentials: falsein your workflow step [3][4]. - Cleanup: The sensitive credential file created in$RUNNER_TEMPis removed by the action during the post-job cleanup phase [3][4]. - Requirements: This mechanism requires Actions Runner version v2.329.0 or later to function correctly, particularly for scenarios involving Docker container actions [4][1]. If you examine your.git/configfile after a checkout with the default settings, you will see entries similar to[includeIf "gitdir:/path/to/repo/.git"]pointing to a configuration file located in your$RUNNER_TEMPpath [6].Citations:
Disable persisted checkout credentials before running repository-controlled scripts.
actions/checkoutpersists the workflow token by default, so addpersist-credentials: falseat each workflow checkout that precedes Rust or PowerShell steps:.github/workflows/ci.yml#L212fuzz checkout.github/workflows/ci.yml#L233rootless-matrix checkout.github/workflows/ci.yml#L245rootless/no-net checkout.github/workflows/ci.yml#L273eval-reproducibility checkout🧰 Tools
🪛 zizmor (1.29.0)
[warning] 212-212: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
📍 Affects 1 file
.github/workflows/ci.yml#L212-L212(this comment).github/workflows/ci.yml#L233-L233.github/workflows/ci.yml#L245-L245.github/workflows/ci.yml#L273-L273🤖 Prompt for AI Agents
Source: Linters/SAST tools