Skip to content

Commit fd37c07

Browse files
authored
Merge pull request #1601 from odeyemitobi/docs/contract-events-alloc-upd-1504
docs: add alloc_upd event schema to CONTRACT_EVENTS.md
2 parents e0ed4a2 + f3fd1f6 commit fd37c07

4 files changed

Lines changed: 196 additions & 0 deletions

File tree

.github/audit-waivers.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"$schema": "Dependency audit waivers. Advisories listed here are accepted risks.",
3+
"_docs": "See docs/DEPENDENCY_AUDIT.md for the waiver process.",
4+
"npm": [],
5+
"cargo": []
6+
}

.github/workflows/dep-scan.yml

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
name: Dependency Scan
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- 'package.json'
7+
- 'package-lock.json'
8+
- 'backend/package.json'
9+
- 'backend/package-lock.json'
10+
- 'frontend/package.json'
11+
- 'frontend/package-lock.json'
12+
- 'contracts/Cargo.toml'
13+
- 'contracts/Cargo.lock'
14+
push:
15+
branches:
16+
- main
17+
paths:
18+
- 'package.json'
19+
- 'package-lock.json'
20+
- 'backend/package.json'
21+
- 'backend/package-lock.json'
22+
- 'frontend/package.json'
23+
- 'frontend/package-lock.json'
24+
- 'contracts/Cargo.toml'
25+
- 'contracts/Cargo.lock'
26+
schedule:
27+
- cron: '0 6 * * *'
28+
workflow_dispatch:
29+
30+
permissions:
31+
contents: read
32+
33+
jobs:
34+
npm-audit:
35+
name: npm audit
36+
runs-on: ubuntu-latest
37+
strategy:
38+
fail-fast: false
39+
matrix:
40+
directory: ['.', 'backend', 'frontend']
41+
steps:
42+
- uses: actions/checkout@v4
43+
44+
- uses: actions/setup-node@v4
45+
with:
46+
node-version-file: '.nvmrc'
47+
48+
- name: Install dependencies
49+
working-directory: ${{ matrix.directory }}
50+
run: npm ci --ignore-scripts
51+
52+
- name: Run npm audit
53+
working-directory: ${{ matrix.directory }}
54+
run: |
55+
WAIVER_FILE="${GITHUB_WORKSPACE}/.github/audit-waivers.json"
56+
EXCLUDE_ARGS=""
57+
if [ -f "$WAIVER_FILE" ]; then
58+
WAIVERS=$(jq -r '.npm[]?.id // empty' "$WAIVER_FILE" 2>/dev/null || true)
59+
for waiver in $WAIVERS; do
60+
EXCLUDE_ARGS="$EXCLUDE_ARGS --exclude $waiver"
61+
done
62+
fi
63+
echo "::group::npm audit output"
64+
npm audit --audit-level=high $EXCLUDE_ARGS || {
65+
echo "::endgroup::"
66+
echo "::error::npm audit found high/critical vulnerabilities in ${{ matrix.directory }}"
67+
echo "If this is an accepted risk, add the advisory ID to .github/audit-waivers.json"
68+
echo "See docs/DEPENDENCY_AUDIT.md for the waiver process."
69+
exit 1
70+
}
71+
echo "::endgroup::"
72+
echo "No new high/critical vulnerabilities in ${{ matrix.directory }}"
73+
74+
cargo-audit:
75+
name: cargo audit
76+
runs-on: ubuntu-latest
77+
steps:
78+
- uses: actions/checkout@v4
79+
80+
- name: Install Rust toolchain
81+
uses: dtolnay/rust-toolchain@stable
82+
83+
- name: Install cargo-audit
84+
run: cargo install cargo-audit
85+
86+
- name: Run cargo audit
87+
working-directory: contracts
88+
run: |
89+
WAIVER_FILE="${GITHUB_WORKSPACE}/.github/audit-waivers.json"
90+
IGNORE_ARGS=""
91+
if [ -f "$WAIVER_FILE" ]; then
92+
WAIVERS=$(jq -r '.cargo[]?.id // empty' "$WAIVER_FILE" 2>/dev/null || true)
93+
for waiver in $WAIVERS; do
94+
IGNORE_ARGS="$IGNORE_ARGS --ignore $waiver"
95+
done
96+
fi
97+
echo "::group::cargo audit output"
98+
cargo audit $IGNORE_ARGS || {
99+
echo "::endgroup::"
100+
echo "::error::cargo audit found vulnerabilities in contracts/"
101+
echo "If this is an accepted risk, add the advisory ID to .github/audit-waivers.json"
102+
echo "See docs/DEPENDENCY_AUDIT.md for the waiver process."
103+
exit 1
104+
}
105+
echo "::endgroup::"
106+
echo "No known vulnerabilities in contracts crate"
107+
108+
- name: Notify on scheduled scan failure
109+
if: failure() && github.event_name == 'schedule'
110+
run: |
111+
echo "::warning::Daily dependency scan found new vulnerabilities."
112+
echo "Review the failing job and update waivers or fix dependencies."
113+
if [ -n "${{ secrets.SLACK_WEBHOOK_URL }}" ]; then
114+
curl -s -X POST -H 'Content-type: application/json' \
115+
--data "{\"text\":\"Dependency Scan Failed\n*Repo*: ${{ github.repository }}\n*Link*: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}" \
116+
"${{ secrets.SLACK_WEBHOOK_URL }}" || true
117+
fi

docs/CONTRACT_EVENTS.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,15 @@ Aligned with `contracts/src/lib.rs` and `contracts/src/portfolio.rs`.
4444
| `portfolio` | `withdraw` | `(portfolio_id, asset, amount)` | `withdraw` |
4545
| `portfolio` | `rebalanced` | `(portfolio_id, timestamp)` | `rebalance_executed` |
4646
| `portfolio` | `cooldown_override` | `(portfolio_id, admin, timestamp)` | (audit only; not indexed by default) |
47+
| `portfolio` | `alloc_upd` | `(portfolio_id, old_allocations, new_allocations)` | `allocation_updated` |
4748
| Topic[0] | Topic[1] | Payload shape (Rust) | Indexed as |
4849
|----------|----------|----------------------|------------|
4950
| `portfolio` | `created` | `(portfolio_id: u64, user: Address)` | `portfolio_created` |
5051
| `portfolio` | `deposit` | `(portfolio_id: u64, asset: Address, amount: i128, memo: String)` | `deposit` |
5152
| `portfolio` | `rebalanced` | `(portfolio_id: u64, current_time: u64)` | `rebalance_executed` |
5253
| `portfolio` | `fee_charged` | `(portfolio_id: u64, recipient: Address, amount: i128)` | `fee_charged` |
5354
| `portfolio` | `upgraded` | `(from_hash: Bytes, to_hash: Bytes, timestamp: u64)` | `contract_upgraded` |
55+
| `portfolio` | `alloc_upd` | `(portfolio_id: u64, old_allocations: Map<Address, u32>, new_allocations: Map<Address, u32>)` | `allocation_updated` |
5456

5557
**Synonyms:** the indexer accepts `rebalance_executed` or `executed` as the second topic for the rebalance event (same payload rules).
5658

@@ -64,9 +66,43 @@ The `deposit` event now includes a `memo: String` field at tuple index `3`. Back
6466
- **Amount (deposit / withdraw):** tuple index `2`.
6567
- **Timestamp (rebalanced / cooldown_override):** tuple index `1` for rebalanced; index `2` for cooldown_override when admin is at index `1`.
6668
- **Memo (deposit):** tuple index `3`, or object keys `memo`.
69+
- **Old allocations (alloc_upd):** tuple index `1`, `Map<Address, u32>` of previous target allocations.
70+
- **New allocations (alloc_upd):** tuple index `2`, `Map<Address, u32>` of updated target allocations.
6771

6872
Events from other contracts or with unknown second topics are skipped without failing the batch.
6973

74+
### `alloc_upd` event details
75+
76+
Emitted by the `update_allocations` entrypoint when a user changes a portfolio's target allocation percentages.
77+
78+
**Contract entrypoint:** `update_allocations(portfolio_id: u64, new_allocations: Map<Address, u32>)` — see [`contracts/src/lib.rs`](../contracts/src/lib.rs) and the [Contract Capability Matrix](CONTRACT_CAPABILITY_MATRIX.md) (`update_allocations` row).
79+
80+
**Emit source:** `contracts/src/events.rs` (`emit_allocation_updated`) and inline in `contracts/src/lib.rs`.
81+
82+
**Sample event payload:**
83+
84+
```json
85+
{
86+
"type": "contract",
87+
"topics": ["portfolio", "alloc_upd"],
88+
"data": {
89+
"portfolio_id": 1,
90+
"old_allocations": {
91+
"CDLZFC...XLM": 40,
92+
"CDMLFK...USDC": 35,
93+
"CBKTPM...BTC": 25
94+
},
95+
"new_allocations": {
96+
"CDLZFC...XLM": 50,
97+
"CDMLFK...USDC": 30,
98+
"CBKTPM...BTC": 20
99+
}
100+
}
101+
}
102+
```
103+
104+
> **Note:** The `events.rs` helper (`emit_allocation_updated`) emits the event with topics `(alloc_upd, invoker, correlation_id)` and payload `portfolio_id`. The inline emit in `lib.rs` uses topics `(portfolio, alloc_upd)` with payload `(portfolio_id, old_allocations, new_allocations)`. Backend indexers should handle both shapes.
105+
70106
## Reusable test fixtures
71107

72108
The contract test suite emits canonical event sequences that backend integration tests can replay.
@@ -91,6 +127,7 @@ The contract test suite emits canonical event sequences that backend integration
91127
| `test_deposit_with_memo.1.json` | `portfolio.created`, `portfolio.deposit` | Deposit with explicit reference memo |
92128
| `test_execute_rebalance_success.1.json` | `portfolio.created`, `portfolio.deposit`, `portfolio.rebalanced` | Full rebalance lifecycle |
93129
| `test_set_fee_config.1.json` | `portfolio.created`, `portfolio.rebalanced`, `portfolio.fee_charged` | Rebalance with fee config enabled |
130+
| `test_update_allocations_success.1.json` | `portfolio.created`, `portfolio.alloc_upd` | Allocation update with old and new maps |
94131

95132
### Exporting fixtures for external use
96133

@@ -114,3 +151,4 @@ The `test_contract_events_fixture_export` test in `contracts/src/test.rs` valida
114151
- `contracts/src/test.rs` — Soroban contract tests that produce event snapshot fixtures.
115152
- `backend/src/test/contractEventSchema.test.ts` — version string parsing and mismatch behavior.
116153
- `contracts/src/test.rs` — Soroban integration tests and snapshot fixtures for contract calls.
154+
- `contracts/src/test.rs` (`test_update_allocations_success`) — Verifies `alloc_upd` event emission with correct old/new allocation maps.

docs/DEPENDENCY_AUDIT.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Dependency Audit Process
2+
3+
## What is Scanned
4+
| Tool | Target | Path |
5+
| --- | --- | --- |
6+
| npm audit | Node.js dependencies | Root (`.`), `backend/`, `frontend/` |
7+
| cargo audit | Rust dependencies | `contracts/` |
8+
9+
## When Scans Run
10+
- **Per-PR**: Scans run on PRs when relevant dependency files (`package.json`, `Cargo.toml`, etc.) are modified.
11+
- **Daily**: A scheduled scan runs every day at 06:00 UTC to catch newly disclosed vulnerabilities.
12+
- **Manual**: Can be triggered via workflow dispatch.
13+
14+
## Handling Failures
15+
If the CI fails due to a dependency vulnerability:
16+
1. Review the workflow logs to identify the vulnerable package.
17+
2. Attempt to update the package to a fixed version.
18+
3. If an update is not feasible or the vulnerability is a known accepted risk, you may add a waiver.
19+
20+
## Waiver Process
21+
Waivers are stored in `.github/audit-waivers.json`. To add a waiver, append an object to the relevant array (`npm` or `cargo`).
22+
23+
Required fields for a waiver:
24+
- `id`: The advisory ID (e.g., from npm or rustsec).
25+
- `reason`: A brief explanation of why this risk is accepted.
26+
- `added_by`: GitHub username of the person adding the waiver.
27+
- `added_date`: Date the waiver was added (YYYY-MM-DD).
28+
- `review_by`: Date by which the waiver should be reviewed (YYYY-MM-DD).
29+
30+
### Reviewing Waivers
31+
Waivers should be periodically reviewed to ensure they are still applicable and that a fix hasn't become available.
32+
33+
## Related Files
34+
- `.github/workflows/dep-scan.yml`
35+
- `.github/audit-waivers.json`

0 commit comments

Comments
 (0)