Security Hardening: AppArmor, Stream Authentication, and Supply Chain Audits
Portal OS operates as a spatial computing environment that streams live compositor output to AR glasses over untrusted network segments, executes foreign x86/Windows binaries for compatibility, and links against a large dependency surface of FFI-heavy crates. These three realities — unencrypted RTP broadcast, foreign-code execution, and deep transitive dependency trees — demand a layered security posture. This page documents the four defensive pillars in production: AppArmor confinement for executable execution control, HMAC-SHA256 stream authentication and pipeline injection prevention for the streaming pipeline, cargo-vet / cargo-deny / SBOM for supply chain integrity, and multi-layer secret scanning with custom static analysis rules.
Sources: SECURITY.md#L1-L70
Security Architecture Overview
Section titled “Security Architecture Overview”The following diagram shows how the four pillars interlock across the build, deployment, and runtime phases:
graph TB
subgraph Build["Build & CI Phase"]
DENY["cargo-deny<br/>advisories · licenses · bans"]
VET["cargo-vet<br/>audits.toml + imports.lock"]
SBOM["cargo-cyclonedx<br/>CycloneDX JSON + XML"]
SEMGREP["Semgrep Rules<br/>5 custom patterns"]
GITLEAKS_CI["Gitleaks CI<br/>PR + push scan"]
TRUFFLE_CI["TruffleHog CI<br/>verified-secrets scan"]
end
subgraph Release["Release Phase"]
COSIGN["cosign keyless<br/>Sigstore / OIDC"]
MINISIGN["minisign<br/>Ed25519 offline key"]
VERIFY["verify_release.sh<br/>round-trip check"]
end
subgraph Runtime["Runtime Phase"]
APPARMOR["AppArmor<br/>portal-x86-restrict"]
HMAC["HMAC-SHA256<br/>per-packet RTP auth"]
VALIDATE["validate_safe_token<br/>pipeline injection guard"]
SYSTEMD["Systemd Hardening<br/>NoNewPrivileges · ProtectSystem"]
end
subgraph Monitoring["Continuous Monitoring"]
TRUFFLE_DAILY["TruffleHog Daily<br/>per-subsystem scan"]
TRUFFLE_WEEKLY["TruffleHog Weekly<br/>full-repo scan"]
DEPENDABOT["Dependabot<br/>weekly dep updates"]
CARGO_AUDIT["cargo-audit<br/>daily RustSec check"]
end
DENY --> RELEASE_BINARY["Signed Release Binaries"]
VET --> RELEASE_BINARY
SBOM --> RELEASE_BINARY
COSIGN --> RELEASE_BINARY
MINISIGN --> RELEASE_BINARY
VERIFY --> RELEASE_BINARY
RELEASE_BINARY --> APPARMOR
RELEASE_BINARY --> HMAC
RELEASE_BINARY --> VALIDATE
RELEASE_BINARY --> SYSTEMD
SEMGREP -.-> DENY
GITLEAKS_CI -.-> TRUFFLE_CI
TRUFFLE_DAILY --> MONITORING
TRUFFLE_WEEKLY --> MONITORING
DEPENDABOT --> MONITORING
CARGO_AUDIT --> MONITORING
AppArmor: Confining Foreign Executable Execution
Section titled “AppArmor: Confining Foreign Executable Execution”The Portal platform runs Wine for x86/Windows application compatibility. Without confinement, a malicious .exe downloaded by a user could execute anywhere on the filesystem. The AppArmor profile portal-x86-restrict establishes a deny-by-default, allow-by-exception policy that blocks Windows and x86 executable execution outside managed directories.
The profile operates with explicit deny rules for executable file extensions across home directories and temporary paths, while allowing execution only from curated managed folders:
| Rule Pattern | Action | Rationale |
|---|---|---|
/home/**.exe, .EXE, .msi, .MSI, .bat, .cmd |
Deny execute | Block arbitrary Windows binaries in user space |
/tmp/**.exe, /var/tmp/**.exe |
Deny execute | Prevent execution from transient download locations |
/home/*/Applications/Windows/** |
Allow execute (inherit) | Managed Wine application directory |
/home/*/Applications/x86_Linux/** |
Allow execute (inherit) | Managed x86 Linux binary directory |
The profile uses ABI version 4.0 and includes the abstractions/base template for standard system access patterns. The ixr permission flags on allow rules mean inherit-execute (the binary runs under the same profile), ensuring the restriction follows into any child processes spawned by the allowed executables.
Sources: portal/apparmor/portal-x86-restrict#L1-L26
Stream Authentication: HMAC-SHA256 Per-Packet Integrity
Section titled “Stream Authentication: HMAC-SHA256 Per-Packet Integrity”Threat Model
Section titled “Threat Model”The Portal streaming daemon captures compositor output via DMA-BUF, encodes it to H.265, and broadcasts RTP/UDP packets to AR glasses on the local access point. Without authentication, a man-in-the-middle attacker could inject forged RTP packets — corrupting the rendered frame or injecting crafted H.265 NAL units that trigger decoder vulnerabilities. The HMAC-SHA256 mechanism provides per-packet integrity so the glasses receiver can reject any packet whose tag fails verification.
Implementation Architecture
Section titled “Implementation Architecture”The authentication system has three components working in sequence:
sequenceDiagram
participant Env as Environment Variable
participant Main as portal_stream.c main()
participant Probe as hmac_pad_probe()
participant HMAC as hmac_util.c
participant Sink as udpsink (GStreamer)
Env->>Main: PORTAL_STREAM_HMAC_KEY (64 hex chars)
Main->>Main: portal_hex_decode_key() → 32-byte key
Main->>Main: G.hmac_enabled = 1
Main->>Sink: Attach probe to udpsink sink pad
loop Each RTP packet
Sink->>Probe: GstBuffer (complete RTP packet)
Probe->>Probe: gst_buffer_make_writable()
Probe->>Probe: Stage 1: gst_buffer_map (READ)
Probe->>HMAC: portal_compute_hmac_sha256(key, data, len)
HMAC-->>Probe: 32-byte tag
Probe->>Probe: Stage 2: gst_rtp_buffer_map (RW)
Probe->>Probe: add_extension_twobyte_header(app_id=1, tag)
Probe-->>Sink: Modified buffer with HMAC extension
end
HMAC Computation (hmac_util.c)
Section titled “HMAC Computation (hmac_util.c)”The HMAC helper wraps OpenSSL’s HMAC() one-shot API with EVP_sha256 as the digest. The implementation validates all inputs defensively — NULL key, NULL output buffer, and NULL data with non-zero length are all rejected with return value 0. The hex key decoder rejects malformed input (wrong length, non-hex characters) at startup, preventing a garbled key from silently producing incorrect tags.
The key constants follow RFC 2104 / FIPS 198-1 recommendations:
| Constant | Value | Meaning |
|---|---|---|
PORTAL_HMAC_KEY_LEN |
32 bytes | Recommended HMAC-SHA256 key size |
PORTAL_HMAC_TAG_LEN |
32 bytes | Full HMAC-SHA256 digest length |
PORTAL_HMAC_KEY_HEX_LEN |
64 chars | Hex-encoded key length |
Sources: portal/streaming/hmac_util.c#L1-L68, portal/streaming/hmac_util.h#L1-L45
RTP Extension Embedding
Section titled “RTP Extension Embedding”The HMAC tag is embedded as an RFC 5285 two-byte header extension (profile 0x100, app id 1). The two-byte form is mandatory because the 32-byte HMAC-SHA256 tag exceeds the one-byte form’s 16-byte per-element limit. Receivers that do not validate the extension simply ignore it per RFC 3550 §5.3.1, preserving backward compatibility with unauthenticated glasses.
The probe uses a two-stage map pattern: first gst_buffer_map reads the raw on-wire packet bytes (header + payload) for HMAC input, then gst_rtp_buffer_map writes the extension into the RTP structure. This separation is necessary because GstBuffer provides raw bytes while GstRTPBuffer understands RTP framing semantics.
Fail-Closed Policy
Section titled “Fail-Closed Policy”The HMAC pad probe implements a strict fail-closed policy — if any step in the authentication chain fails, the packet is dropped and logged, never sent unauthenticated:
| Failure Condition | Probe Action | Rationale |
|---|---|---|
| Buffer not writable | DROP + log | Cannot guarantee buffer ownership |
gst_buffer_map fails |
DROP + log | Cannot read packet bytes for HMAC |
portal_compute_hmac_sha256 fails |
DROP + log | Never send a packet that should be tagged but wasn’t |
gst_rtp_buffer_map fails |
DROP + log | Cannot write extension into RTP structure |
If the PORTAL_STREAM_HMAC_KEY environment variable is absent or malformed at startup, the daemon falls back to unauthenticated mode with a warning. This preserves v0.2.x backward compatibility but logs an explicit (insecure) annotation. The typed error code STREAM_ERR_AUTH (value 4) is defined for future enforcement.
Sources: portal/streaming/portal_stream.c#L452-L534, portal/streaming/portal_stream.h#L1-L20, portal/streaming/portal_stream.c#L697-L714
Test Coverage
Section titled “Test Coverage”The HMAC implementation has comprehensive cmocka unit tests with RFC 4231 known-answer test vectors:
| Test | Validates |
|---|---|
| RFC 4231 §4.2 (Case 1) | Key 0x0b*20, data “Hi There” → expected digest |
| RFC 4231 §4.3 (Case 2) | Key “Jefe”, data “what do ya want for nothing?” → expected digest |
| NULL key rejection | Returns 0, no crash |
| NULL output buffer | Returns 0, no crash |
| NULL data + non-zero length | Returns 0 (defensive contract) |
| Zero-length data | Deterministic digest (HMAC of inner pad only) |
| Valid hex decode | 64 hex chars → correct 32-byte key |
| Short hex (63 chars) | Rejected with return 0 |
| Long hex (65 chars) | Rejected with return 0 |
| Non-hex character (‘g’) | Rejected with return 0 |
Sources: portal/streaming/tests/test_hmac_util.c#L1-L200
Pipeline Injection Prevention: validate_safe_token
Section titled “Pipeline Injection Prevention: validate_safe_token”Before the GStreamer pipeline string is constructed via snprintf, every externally-sourced token (host, port, bind address, multicast interface) passes through a metacharacter rejection filter. The validate_safe_token function rejects any string containing shell-injection characters that could corrupt the GStreamer pipeline DSL:
| Rejected Character | Injection Vector |
|---|---|
; |
Command separator (e.g., host;rm) |
| |
Pipeline injection (e.g., a|b) |
$ |
Shell variable expansion (e.g., $HOME) |
` |
Command substitution (e.g., a`b`) |
If any token fails validation, setup_pipeline returns STREAM_ERR_CONFIG before gst_parse_launch is ever called. The test suite covers 12 cases including infix injection (192.168.1.1;rm -rf /), legitimate values (IPv4 addresses, ports, interface names, Wayland output names with hyphens), and NULL/empty rejection.
Sources: portal/streaming/portal_stream.c#L444-L450, portal/streaming/portal_stream.c#L557-L564, portal/streaming/tests/test_validate_safe_token.c#L1-L161
Supply Chain Audits: cargo-vet, cargo-deny, and SBOM
Section titled “Supply Chain Audits: cargo-vet, cargo-deny, and SBOM”cargo-deny: Multi-Dimensional Gate
Section titled “cargo-deny: Multi-Dimensional Gate”The deny.toml configuration enforces four checks as CI release gates: advisories, licenses, bans, and sources. Each ignored advisory carries a human-readable reason and re-verification date.
| Check Category | Policy | Enforcement |
|---|---|---|
| Advisories | Deny all RustSec classes (vulns, unmaintained, unsound, yanked) | 9 advisories ignored — each with reason + verification date |
| Licenses | Allow-list of 14 approved licenses; confidence threshold 0.93 | 28 workspace crates clarified as MIT |
| Bans | multiple-versions = "deny" with documented skip entries |
26 skip entries, grouped by ecosystem split cause |
| Sources | unknown-registry = "deny", unknown-git = "deny" |
No crates.io之外的registry |
The ignored advisories are carefully categorized by transitive dependency path. For example, RUSTSEC-2023-0089 (atomic-polyfill unmaintained) traces through heapless v0.7 → postcard v1.1 → pcp-stream, pcp-ipc, portal-input, portal-spatial, portal-voice, with no upgrade path until heapless releases a new version. Each was verified as still in-tree on 2026-07-20 via cargo tree -i.
Sources: deny.toml#L1-L21, deny.toml#L211-L382, deny.toml#L23-L50
cargo-vet: Formal Dependency Auditing
Section titled “cargo-vet: Formal Dependency Auditing”The supply-chain/ directory implements Mozilla’s cargo-vet framework with a tiered audit priority system. The project imports audit attestations from Google and Mozilla as cascade trust — crates already vetted by these organizations are transitively trusted without duplicate review.
The audit priority document identifies the top 20 highest-risk dependencies across six tiers ranked by blast radius:
| Tier | Risk Category | Example Crates | Audit Status |
|---|---|---|---|
| Tier 1 | FFI + Unsafe (memory safety boundary) | libc, nix, libloading, evdev, wayland-backend |
8 crates audited |
| Tier 2 | Network I/O (attack surface) | mio, socket2, signal-hook-registry |
3 crates audited |
| Tier 3 | Cryptographic primitives | ed25519-dalek, sha2, getrandom |
3 crates audited |
| Tier 4 | Concurrency + Data | parking_lot, crossbeam-channel, dashmap |
3 crates audited |
| Tier 5 | Build-time code generation | bindgen, cbindgen |
2 crates audited |
| Tier 6 | Database (SQL injection) | rusqlite |
1 crate audited |
Each audited crate in audits.toml records the version, criteria (safe-to-deploy or safe-to-run), and review notes documenting what was examined. For example, the ed25519-dalek audit notes constant-time implementation review, RFC 8032 compliance verification, and Mozilla’s formal audit at v2.1.0.
Sources: supply-chain/audit-priorities.md#L1-L99, supply-chain/audits.toml#L1-L88, supply-chain/config.toml#L1-L12
SBOM Generation
Section titled “SBOM Generation”Release builds produce CycloneDX 1.5 SBOMs in both JSON and XML formats via cargo-cyclonedx. The SBOM is generated per-crate and uploaded as a release artifact alongside the signed binaries. The generate_sbom.sh script handles collection from nested crate directories into a clean output directory.
Sources: scripts/generate_sbom.sh#L1-L127, .github/workflows/security.yml#L32-L45
Release Artifact Signing: Dual Signature Scheme
Section titled “Release Artifact Signing: Dual Signature Scheme”Portal uses a dual-signature scheme to ensure artifact authenticity even if one signing infrastructure is compromised:
| Scheme | Algorithm | Key Management | Transparency |
|---|---|---|---|
| cosign (Sigstore) | Ed25519 | Keyless via GitHub OIDC | Rekor transparency log |
| minisign | Ed25519 | Offline secret key (hardware token) | Public key in keys/portal-release.pub |
The release workflow (release.yml) signs five production binaries: portal-stream, portal-voiced, portal-llmd, portal-keyboardd, and portal-wm. Each binary receives .sig (raw signature), .pem (Fulcio certificate), and .bundle (Rekor entry) from cosign, plus .minisig from minisign. SBOM files (.cdx.json, .cdx.xml) are also signed by both schemes.
The key policy mandates that secret keys are never committed and are generated on an offline machine. Trust bootstrapping uses cosign’s keyless signatures until minisign trust is established, after which cosign may be retired.
Sources: .github/workflows/release.yml#L1-L136, scripts/sign_release.sh#L1-L168, scripts/cosign-sign.sh#L1-L167, keys/README.md#L1-L60
Secret Detection: Multi-Layer DLP Defense
Section titled “Secret Detection: Multi-Layer DLP Defense”Portal implements secret detection at three temporal layers — pre-commit, CI gate, and scheduled monitoring — using two complementary tools with different detection philosophies:
graph LR
subgraph Pre-Commit["Pre-Commit (Local)"]
GL_PC["Gitleaks protect<br/>staged changes only"]
TH_PC["TruffleHog<br/>HEAD diff, verified-only"]
end
subgraph CI["CI Gate (Per PR/Push)"]
GL_CI["Gitleaks Action<br/>full history"]
TH_CI["TruffleHog Docker<br/>verified, fail on find"]
end
subgraph Scheduled["Scheduled Monitoring"]
TH_DAILY["TruffleHog Daily<br/>per-subsystem matrix"]
TH_WEEKLY["TruffleHog Weekly<br/>full-repo history"]
end
DEV["Developer"] --> Pre-Commit
PR["Pull Request"] --> CI
CRON["Cron Schedule"] --> Scheduled
| Layer | Tool | Scope | Detection Method | False Positive Rate |
|---|---|---|---|---|
| Pre-commit | Gitleaks | Staged files only | Regex + entropy | Low-moderate |
| Pre-commit | TruffleHog | HEAD → working tree | Verified against live APIs | Near-zero |
| CI gate | Gitleaks | Full git history | Regex + entropy | Low-moderate |
| CI gate | TruffleHog | Full history, PR-scoped | Verified against live APIs | Near-zero |
| Daily | TruffleHog | Per-subsystem matrix | Verified, creates GitHub issue | Near-zero |
| Weekly | TruffleHog | Full repository | Verified, creates GitHub issue | Near-zero |
Custom Gitleaks Rules
Section titled “Custom Gitleaks Rules”Beyond the default Gitleaks ruleset, Portal defines six custom detection patterns targeting platform-specific secret categories: Wayland socket paths, SSH private key blocks, streaming IP:port endpoints, DRM device paths, extended GitHub token patterns, and high-entropy internal IP:port combinations. Each rule includes carefully scoped allowlists for test fixtures, documentation, compositor configs, and lock files.
Sources: .gitleaks.toml#L1-L170, .pre-commit-config.yaml#L1-L35, .github/workflows/gitleaks-scan.yml#L1-L26, .github/workflows/trufflehog-scan.yml#L1-L25, .github/workflows/trufflehog-daily-subsystem.yml#L1-L119, .github/workflows/trufflehog-weekly-repo.yml#L1-L64
Custom Static Analysis: Semgrep Rules
Section titled “Custom Static Analysis: Semgrep Rules”Five Portal-specific Semgrep rules target vulnerability patterns unique to the codebase’s FFI, streaming, and compositor integration:
| Rule ID | Language | Severity | Mode | Detection Target |
|---|---|---|---|---|
gstreamer-pipeline-injection |
C | ERROR | Taint | argv → gst_parse_launch data flow |
strncpy-unterminated |
C | WARNING | Pattern | Missing null terminator after strncpy |
unsafe-extern-no-catch-unwind |
Rust | WARNING | Pattern | Panic through C frames in extern "C" callbacks |
warp-renderer-no-drop |
Rust | WARNING | Pattern | GL resource leak (missing Drop impl) |
frame-cancel-no-backoff |
C | INFO | Pattern | Infinite retry loop without backoff |
The taint-mode gstreamer-pipeline-injection rule is particularly significant — it traces data flow from argv parameters through to gst_parse_launch calls, complementing the runtime validate_safe_token guard with compile-time detection. Each rule includes test files with both vulnerable and safe code patterns for validation.
Sources: .semgrep-rules/RULE_REPORT.md#L1-L58
Dependency Risk Assessment
Section titled “Dependency Risk Assessment”A comprehensive supply chain risk audit evaluated all ~90 direct Rust dependencies plus vendored C/C++ code against six risk criteria. 31 dependencies were flagged with at least one risk factor, with the dominant concerns being absent security contacts (28 crates) and low popularity under 500 GitHub stars (17 crates).
The most actionable findings were three replacement targets: dirs (archived) → directories v5, urlencoding (low popularity) → percent-encoding, and lazy_static (nursery) → std::sync::LazyLock. The unavoidable high-risk dependencies — drm, gbm, evdev, alsa, rknn-sys, whisper.cpp — are hardware-specific with no alternatives, and are mitigated through version pinning, lockfile review, and the cargo-vet audit process.
Sources: .supply-chain-risk-auditor/results.md#L1-L100
Sharp-Edges Analysis
Section titled “Sharp-Edges Analysis”A separate sharp-edges audit identified five HIGH-severity pitfalls in the FFI and GPU integration code. These are not actively exploited vulnerabilities but represent trapdoors for future developers:
| Finding | Category | Risk | Mitigation |
|---|---|---|---|
SE-001: rknn_input.buf raw pointer with DMA flag |
Primitive vs Semantic | NPU DMA reads past allocation | Builder pattern with DMA-safe buffer validation |
SE-002: RkllmContext::run() always returns empty string |
Silent Failure | Success masquerades as empty result | Collect callback output via Arc<Mutex<String>> |
SE-003: RkllmCallback panic through C frames |
Primitive vs Semantic | Undefined behavior on unwind | catch_unwind + abort handler |
SE-004: Dual rknn_param default sources |
Dangerous Default | Rust vs C SDK defaults disagree | Remove Rust Default impl |
SE-005: WarpRenderer no Drop |
Silent Failure | Permanent GL resource leak on embedded GPU | Implement Drop that calls cleanup() |
Sources: .supply-chain-risk-auditor/sharp-edges-report.md#L1-L80
Insecure Defaults Audit: Hardening Findings
Section titled “Insecure Defaults Audit: Hardening Findings”A static analysis pass across all systemd services, compositor config, and the streaming C codebase identified 14 findings (2 CRITICAL, 4 HIGH, 5 MEDIUM, 3 LOW). The audit confirmed strong negative results: no hardcoded passwords, no 0.0.0.0 bind-all, no weak crypto, no world-writable file permissions, no CORS wildcards, and no debug_assertions gating security checks.
The two CRITICAL findings — hardcoded IP fallback in the streaming binary and hardcoded bind address in the GStreamer pipeline string — were both remediated in the v0.2.1 hardening pass. The fix eliminated all hardcoded deployment IPs from source, requiring PORTAL_STREAM_TARGET to be provided via environment variable or argv, with non-zero exit on absence.
| Finding | Severity | Status | Remediation |
|---|---|---|---|
| FID-001: Hardcoded IP fallback | CRITICAL | Fixed (v0.2.1) | No DEFAULT_HOST; fail-loud if env var absent |
| FID-002: Hardcoded bind address | CRITICAL | Fixed (v0.2.1) | Bind address from PORTAL_STREAM_BIND env var |
| FID-003: Unencrypted RTP stream | HIGH | Mitigated by HMAC | HMAC-SHA256 per-packet authentication added |
| FID-004: No receiver authentication | HIGH | Mitigated by HMAC | Receivers can validate HMAC extension |
| FID-005: portal-input runs as root | HIGH | Tracked | Documented; udev rules as future work |
| FID-006: portal-file-watcherd no hardening | HIGH | Tracked | Apply portal-launcher.service hardening template |
Sources: docs/INSECURE_DEFAULTS_AUDIT.md#L1-L250
Security Reporting Infrastructure
Section titled “Security Reporting Infrastructure”Vulnerability Disclosure
Section titled “Vulnerability Disclosure”The project maintains a formal vulnerability disclosure program with severity-based response timelines and PGP-encrypted reporting. The SECURITY.md defines supported versions (v0.2.0 current, v0.1.0 EOL) and a Safe Harbor clause under the Demain Source License §6 permitting security research in good faith.
| Severity | Acknowledgment | Resolution Target |
|---|---|---|
| Critical | 48 hours | 90 days |
| High | 5 business days | 120 days |
| Medium | 7 business days | Next release |
| Low | 7 business days | Best effort |
The .well-known/security.txt follows RFC 9116, publishing the contact address, expiry date, preferred languages (en, fr), and canonical URL. Dependabot runs weekly across GitHub Actions, Cargo, and Docker ecosystems with scoped PR labels and commit message prefixes.
Sources: SECURITY.md#L1-L70, .well-known/security.txt#L1-L8, .github/dependabot.yml#L1-L51
CI Security Pipeline Summary
Section titled “CI Security Pipeline Summary”The following table consolidates all security-related CI workflows:
| Workflow | Trigger | Tool | Scope |
|---|---|---|---|
security.yml → cargo-audit |
Push, PR, daily cron | rustsec/audit-check | RustSec advisory database |
security.yml → cargo-deny |
Push, PR | EmbarkStudios/cargo-deny | Advisories + licenses + bans + sources |
security.yml → SBOM |
Tag push only | cargo-cyclonedx | CycloneDX JSON + XML per crate |
gitleaks-scan.yml |
Push, PR | gitleaks-action | Full git history, pattern + entropy |
trufflehog-scan.yml |
Push, PR | trufflehog Docker | Full history, verified-only |
trufflehog-daily-subsystem.yml |
Daily cron | trufflehog Docker | 18-subsystem matrix scan |
trufflehog-weekly-repo.yml |
Weekly cron (Mon) | trufflehog Docker | Full repository, all paths |
release.yml |
Tag push (v*) | cosign + minisign | Binary + SBOM signing |
miri.yml |
(Configured) | Miri | Undefined behavior in unsafe Rust |
All workflow actions use pinned commit SHAs (not floating tags) to prevent supply chain attacks on the CI pipeline itself, and persist-credentials: false prevents token leakage to subsequent steps.
Sources: .github/workflows/security.yml#L1-L45, .github/workflows/release.yml#L1-L136
Next Steps
Section titled “Next Steps”- Learn how the systemd services apply kernel-level hardening (
NoNewPrivileges,ProtectSystem,SystemCallFilter) in Systemd Service Management and Network Topology - Understand the full streaming pipeline that this authentication protects in RTP Packetization and HMAC-SHA256 Stream Authentication
- Explore the CI/CD pipeline that enforces these checks at build time in CI/CD Pipeline: Lint Enforcement, Cross-Compilation, and Release Signing
- Review the testing infrastructure that validates security-critical code paths in Testing Strategy: Unit Tests, Property Tests, Fuzzing, and Benchmarks