Skip to content

Commit 9d21708

Browse files
authored
Merge pull request #616 from SYMBAxx/audit/issues-610-611-612-613
fix/docs: disabled nav link semantics + SECURITY.md merge-corruption (#610, #611)
2 parents 11d7f11 + 0c72af6 commit 9d21708

3 files changed

Lines changed: 124 additions & 132 deletions

File tree

‎SECURITY.md‎

Lines changed: 38 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,30 @@ FlowStar handles token streaming workflows on Stellar and Soroban. Please report
44
suspected vulnerabilities privately so maintainers have time to investigate and
55
coordinate a fix before details become public.
66

7-
## Supported Scope
7+
## Scope
88

99
Security reports are in scope when they affect FlowStar-controlled code or
1010
deployment configuration, including:
1111

12-
- Soroban smart contracts in `contracts/`, including authorization, token
13-
accounting, stream lifecycle logic, and storage behavior.
14-
- Frontend and wallet integration code that prepares, signs, or submits FlowStar
15-
transactions.
12+
- Soroban smart contracts in `contracts/` (currently `contracts/streaming/`),
13+
including authorization, token accounting, stream lifecycle logic, and
14+
storage behavior — logic errors, authorization bypasses, fund-loss vectors,
15+
integer overflow/underflow.
16+
- Frontend and wallet integration code (`app/`, `components/`, `hooks/`,
17+
`lib/`) that prepares, signs, or submits FlowStar transactions — XSS, CSRF,
18+
wallet-key exposure, data leakage.
1619
- API routes, RPC integration, transaction building, and client-side stream
1720
calculations that can affect user funds or permissions.
1821
- CI/CD workflows, deployment scripts, dependency configuration, and secret
19-
handling owned by this repository.
22+
handling owned by this repository — supply-chain attacks, secret exposure in
23+
workflows.
2024

2125
The following are normally out of scope:
2226

23-
- Vulnerabilities in the Stellar network, Soroban platform, Freighter wallet, or
24-
third-party services unless they are caused by FlowStar integration code.
25-
- General dependency reports without a demonstrated exploit path in FlowStar.
27+
- Vulnerabilities in the Stellar network, Soroban platform, Freighter wallet,
28+
or third-party services unless they are caused by FlowStar integration code.
29+
- General dependency reports without a demonstrated exploit path in FlowStar
30+
(report those to the dependency's own maintainers).
2631
- Social engineering, phishing, physical attacks, spam, or denial-of-service
2732
testing that degrades public infrastructure.
2833
- Issues that only affect a local development environment without impact on
@@ -48,14 +53,14 @@ include exploit details in that issue.
4853

4954
Maintainers should aim for the following response windows:
5055

51-
| Step | Target |
52-
| --- | --- |
53-
| Acknowledge report | Within 48 hours |
54-
| Initial triage | Within 1 week |
55-
| Critical fix or mitigation | Within 72 hours after confirmation |
56-
| High severity fix or mitigation | Within 1 week after confirmation |
57-
| Medium severity fix or mitigation | Within 2 weeks after confirmation |
58-
| Low severity fix | Best effort, based on project priorities |
56+
| Step | Target |
57+
| ------------------------------------------ | --------------------------------------------------------------- |
58+
| Acknowledge report | Within 48 hours |
59+
| Initial triage and severity classification | Within 1 week |
60+
| Critical fix or mitigation | Within 72 hours after triage |
61+
| High severity fix or mitigation | Within 1 week after triage |
62+
| Medium severity fix or mitigation | Within 2 weeks after triage |
63+
| Low severity fix | Best effort — typically bundled into the next scheduled release |
5964

6065
Complex smart contract issues may require more time for testing, deployment, or
6166
coordinated disclosure. Maintainers should keep the reporter updated when the
@@ -65,12 +70,12 @@ timeline changes.
6570

6671
Use the following guide when estimating impact:
6772

68-
| Severity | Examples |
69-
| --- | --- |
70-
| Critical | Unauthorized withdrawal or cancellation of streams, theft or permanent loss of user funds, private key or secret exposure, exploit that affects deployed contracts at scale |
71-
| High | Authorization bypass, transaction manipulation, incorrect accounting that can move funds under specific conditions, CI/CD compromise path |
72-
| Medium | Incorrect balances or stream state display that can mislead users into signing harmful transactions, limited data exposure, dependency issue with a practical FlowStar exploit path |
73-
| Low | Hardening gaps, missing security headers, informational leakage without direct fund or permission impact |
73+
| Severity | Examples |
74+
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
75+
| Critical | Unauthorized withdrawal or cancellation of streams, theft or permanent loss of user funds, private key or secret exposure, complete contract takeover, exploit that affects deployed contracts at scale |
76+
| High | Authorization bypass, unauthorized access to user data, transaction manipulation, incorrect accounting that can move funds under specific conditions, CI/CD compromise path |
77+
| Medium | Denial of service affecting individual users, incorrect balances or stream state display that can mislead users into signing harmful transactions, data integrity issues without fund loss, limited data exposure, dependency issue with a practical FlowStar exploit path |
78+
| Low | Hardening gaps, missing security headers, minor/informational disclosure without direct fund or permission impact |
7479

7580
CVSS may be used when helpful, but a practical explanation of exploitability and
7681
user impact is more important than a score alone.
@@ -79,107 +84,33 @@ user impact is more important than a score alone.
7984

8085
FlowStar welcomes good-faith security research. If you comply with this policy,
8186
avoid privacy violations, avoid data destruction, do not exfiltrate funds or
82-
secrets, and report vulnerabilities promptly, the project will not pursue legal
83-
action against you for the research activity itself.
87+
secrets, and report vulnerabilities promptly:
88+
89+
- We will not pursue legal action against you for the research activity itself.
90+
- We will not refer you to law enforcement.
91+
- We will work with you to understand and resolve the issue quickly.
8492

8593
Researchers should:
8694

8795
- Test on local environments or testnet whenever possible.
8896
- Stop testing and report immediately if user funds, private data, or project
8997
secrets may be exposed.
90-
- Access only the minimum data needed to prove the issue.
98+
- Access or modify only the minimum data needed to prove the issue.
99+
- Avoid denial-of-service testing or disrupting live services.
91100
- Give maintainers a reasonable opportunity to remediate before public
92101
disclosure.
93102

94103
## Bug Bounty and Recognition
95104

96105
This policy does not create a standing cash bounty or guarantee payment. If a
97106
linked issue, campaign, or external program offers rewards, eligibility,
98-
severity, payout, and disclosure rules are governed by that program. Maintainers
99-
may still credit good-faith reporters in release notes or advisories when both
100-
parties agree.
107+
severity, payout, and disclosure rules are governed by that program. Absent
108+
such a program, maintainers will still publicly credit good-faith reporters in
109+
release notes or advisories, unless the reporter prefers to remain anonymous.
101110

102111
## Maintainer Setup
103112

104113
To make the reporting path work, repository maintainers should enable GitHub
105114
private vulnerability reporting or GitHub Security Advisories for FlowStar. The
106115
private reporting link above should then route researchers away from public
107116
issues and toward coordinated disclosure.
108-
## Scope
109-
110-
The following are **in scope** for vulnerability reports:
111-
112-
- **Smart contract** (`contracts/streaming/`) — logic errors, authorization bypasses, fund-loss vectors, integer overflow/underflow
113-
- **Frontend** (`app/`, `components/`, `hooks/`, `lib/`) — XSS, CSRF, wallet-key exposure, data leakage
114-
- **CI/CD pipeline** — supply-chain attacks, secret exposure in workflows
115-
116-
The following are **out of scope**:
117-
118-
- Vulnerabilities in third-party dependencies (report to their maintainers directly)
119-
- The Stellar network or Soroban runtime itself
120-
- Issues requiring physical access to a user's device
121-
- Social engineering attacks
122-
123-
---
124-
125-
## Reporting a Vulnerability
126-
127-
**Preferred:** Use [GitHub Security Advisories](https://github.com/FlowwStar/FlowStar/security/advisories/new) to open a private advisory. This keeps the report confidential until a fix is ready.
128-
129-
**Email fallback:** For urgent critical issues, email the maintainers at the address listed on the GitHub profile. Include:
130-
1. A clear description of the vulnerability
131-
2. Steps to reproduce or a proof-of-concept
132-
3. Affected component(s) and version/commit hash
133-
4. Your assessment of impact and severity
134-
135-
Please **do not** open a public GitHub issue for security vulnerabilities.
136-
137-
---
138-
139-
## Response Timeline
140-
141-
| Stage | Target |
142-
|---|---|
143-
| Acknowledgment | Within 48 hours |
144-
| Triage & severity classification | Within 1 week |
145-
| Fix (Critical) | Within 72 hours of triage |
146-
| Fix (High) | Within 1 week of triage |
147-
| Fix (Medium) | Within 2 weeks of triage |
148-
| Fix (Low) | Next scheduled release |
149-
150-
We will keep you updated throughout the process and credit you in the release notes unless you prefer to remain anonymous.
151-
152-
---
153-
154-
## Severity Classification
155-
156-
We use a simplified severity scale:
157-
158-
| Severity | Description |
159-
|---|---|
160-
| **Critical** | Direct loss of user funds, private key exposure, or complete contract takeover |
161-
| **High** | Unauthorized access to user data, bypass of core authorization checks |
162-
| **Medium** | Denial of service for individual users, data integrity issues without fund loss |
163-
| **Low** | Minor information disclosure, cosmetic security issues |
164-
165-
---
166-
167-
## Safe Harbor
168-
169-
FlowStar is committed to working with security researchers. If you discover a vulnerability and report it responsibly under this policy:
170-
171-
- We will not pursue legal action against you
172-
- We will not refer you to law enforcement
173-
- We will work with you to understand and resolve the issue quickly
174-
175-
We ask that you:
176-
177-
- Give us reasonable time to respond before public disclosure
178-
- Avoid accessing or modifying user data beyond what is necessary to demonstrate the vulnerability
179-
- Do not perform denial-of-service attacks or disrupt live services
180-
181-
---
182-
183-
## Bug Bounty
184-
185-
There is no formal bug bounty program at this time. We will publicly credit researchers who report valid vulnerabilities (unless anonymity is requested).
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { describe, it, expect, vi } from 'vitest'
2+
import { render, screen } from '@testing-library/react'
3+
import { Navbar } from '@/components/layout/navbar'
4+
5+
vi.mock('next/link', () => ({
6+
default: ({ href, children, ...props }: any) => (
7+
<a href={href} {...props}>
8+
{children}
9+
</a>
10+
),
11+
}))
12+
vi.mock('next/navigation', () => ({ usePathname: () => '/app' }))
13+
vi.mock('next-themes', () => ({ useTheme: () => ({ setTheme: vi.fn() }) }))
14+
vi.mock('@/components/providers/network-provider', () => ({
15+
useNetwork: () => ({ network: 'testnet', setNetwork: vi.fn() }),
16+
}))
17+
vi.mock('@/components/layout/notification-bell', () => ({ NotificationBell: () => null }))
18+
vi.mock('@/components/layout/connect-wallet-button', () => ({ ConnectWalletButton: () => null }))
19+
20+
const useWalletContextMock = vi.fn()
21+
vi.mock('@/components/providers/wallet-provider', () => ({
22+
useWalletContext: () => useWalletContextMock(),
23+
}))
24+
25+
describe('Navbar — "New stream" link during a network mismatch', () => {
26+
it('renders a real Link when the wallet network matches', () => {
27+
useWalletContextMock.mockReturnValue({
28+
networkMismatch: false,
29+
walletNetwork: 'testnet',
30+
isConnected: true,
31+
})
32+
33+
render(<Navbar />)
34+
35+
const link = screen.getByRole('link', { name: /new stream/i })
36+
expect(link).toHaveAttribute('href', '/app/create')
37+
})
38+
39+
it('renders a real disabled <button> (no href) instead of a Link when the wallet is on the wrong network', () => {
40+
// A `disabled` prop on a Base UI `Button asChild` wrapping a `<Link>` does
41+
// not give the resulting <a> real disabled semantics: `:disabled` CSS
42+
// never matches an anchor, it stays in the tab order, and its `href`
43+
// remains live for middle-click/ctrl-click to open in a new tab. Render
44+
// a real disabled <button> instead so the control has no href at all.
45+
useWalletContextMock.mockReturnValue({
46+
networkMismatch: true,
47+
walletNetwork: 'mainnet',
48+
isConnected: true,
49+
})
50+
51+
render(<Navbar />)
52+
53+
expect(screen.queryByRole('link', { name: /new stream/i })).not.toBeInTheDocument()
54+
55+
const button = screen.getByRole('button', { name: /new stream/i })
56+
expect(button.tagName).toBe('BUTTON')
57+
expect(button).toBeDisabled()
58+
expect(button).not.toHaveAttribute('href')
59+
})
60+
})

‎components/layout/navbar.tsx‎

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@ export function Navbar() {
3939
<nav className="hidden items-center gap-1 md:flex">
4040
{NAV_LINKS.map((link) => {
4141
const active =
42-
link.href === '/app'
43-
? pathname === '/app'
44-
: pathname.startsWith(link.href)
42+
link.href === '/app' ? pathname === '/app' : pathname.startsWith(link.href)
4543
return (
4644
<Link
4745
key={link.href}
@@ -60,33 +58,38 @@ export function Navbar() {
6058
</nav>
6159

6260
<div className="ml-auto flex items-center gap-2">
63-
<Button
64-
asChild
65-
variant="ghost"
66-
size="sm"
67-
className="gap-1.5"
68-
disabled={networkMismatch}
69-
>
70-
<Link href="/app/create">
61+
{networkMismatch ? (
62+
<Button
63+
variant="ghost"
64+
size="sm"
65+
className="gap-1.5"
66+
disabled
67+
aria-label="New stream (unavailable — wallet is on the wrong network)"
68+
>
7169
<Plus className="size-4" />
7270
<span className="hidden sm:inline">New stream</span>
73-
</Link>
74-
</Button>
71+
</Button>
72+
) : (
73+
<Button asChild variant="ghost" size="sm" className="gap-1.5">
74+
<Link href="/app/create">
75+
<Plus className="size-4" />
76+
<span className="hidden sm:inline">New stream</span>
77+
</Link>
78+
</Button>
79+
)}
7580

7681
<DropdownMenu>
7782
<DropdownMenuTrigger asChild>
7883
<Button variant="ghost" size="sm" className="gap-1.5">
7984
<Network className="size-4" />
80-
<span className="hidden sm:inline">{network === 'mainnet' ? 'Mainnet' : 'Testnet'}</span>
85+
<span className="hidden sm:inline">
86+
{network === 'mainnet' ? 'Mainnet' : 'Testnet'}
87+
</span>
8188
</Button>
8289
</DropdownMenuTrigger>
8390
<DropdownMenuContent align="end">
84-
<DropdownMenuItem onClick={() => setNetwork('testnet')}>
85-
Testnet
86-
</DropdownMenuItem>
87-
<DropdownMenuItem onClick={() => setNetwork('mainnet')}>
88-
Mainnet
89-
</DropdownMenuItem>
91+
<DropdownMenuItem onClick={() => setNetwork('testnet')}>Testnet</DropdownMenuItem>
92+
<DropdownMenuItem onClick={() => setNetwork('mainnet')}>Mainnet</DropdownMenuItem>
9093
</DropdownMenuContent>
9194
</DropdownMenu>
9295

@@ -125,18 +128,16 @@ export function Navbar() {
125128
<AlertTriangle className="size-4 shrink-0" />
126129
<span>
127130
Your wallet is on <strong>{walletNetwork}</strong> — please switch to{' '}
128-
<strong>{network}</strong> in Freighter settings. Transactions are disabled until you switch.
131+
<strong>{network}</strong> in Freighter settings. Transactions are disabled until you
132+
switch.
129133
</span>
130134
</div>
131135
)}
132136

133137
{/* Mobile nav */}
134138
<nav className="flex items-center gap-1 border-t border-border px-4 py-2 md:hidden">
135139
{NAV_LINKS.map((link) => {
136-
const active =
137-
link.href === '/app'
138-
? pathname === '/app'
139-
: pathname.startsWith(link.href)
140+
const active = link.href === '/app' ? pathname === '/app' : pathname.startsWith(link.href)
140141
return (
141142
<Link
142143
key={link.href}

0 commit comments

Comments
 (0)