Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
821a005
[auto-update] Update Ragger snapshots
github-actions[bot] Feb 20, 2026
f03e549
Merge pull request #107 from LedgerHQ/auto/update_snapshots-821a005
cedelavergne-ledger Feb 20, 2026
eb78e0e
Align workflows
cedelavergne-ledger Feb 20, 2026
468ff6d
Bump version
cedelavergne-ledger Apr 23, 2026
fa957f6
Use rather nbgl_useCaseKeyboard
cedelavergne-ledger Feb 20, 2026
3d310f2
Update Copyright in App infos
cedelavergne-ledger May 22, 2026
36e71b4
Display a popup when no passwords to delete
cedelavergne-ledger May 22, 2026
d5de37e
Reject creation if nickname already exists
cedelavergne-ledger May 22, 2026
c6b1580
Limit action list when no passwords are available
cedelavergne-ledger May 22, 2026
750eacd
Fix buggy settings
cedelavergne-ledger May 22, 2026
a6cf28e
Fix minor warnings in src
cedelavergne-ledger May 25, 2026
c55ad73
Rework first-launch keyboard layout selection flow
cedelavergne-ledger May 26, 2026
7b3d72d
Align tests and add Nano support
cedelavergne-ledger May 22, 2026
2a1216b
Update snapshots
cedelavergne-ledger May 22, 2026
244f8b3
Add new unit tests
cedelavergne-ledger May 25, 2026
e19c0e2
Update CHANGELOG
cedelavergne-ledger May 25, 2026
b9e5428
Merge pull request #108 from LedgerHQ/cev/useCaseKeyboard
cedelavergne-ledger Jun 1, 2026
dc8bd20
Fix deprecated action
cedelavergne-ledger Jun 1, 2026
17433d3
Add workflow permission
cedelavergne-ledger Jun 1, 2026
581ef46
Update CHANGELOG
cedelavergne-ledger Jun 1, 2026
9063d51
Merge pull request #110 from LedgerHQ/cev/update_workflow
cedelavergne-ledger Jun 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ on:
- develop
pull_request:

permissions:
contents: write
actions: write
pull-requests: write

jobs:
build:
name: Build application using the reusable workflow
Expand Down
35 changes: 7 additions & 28 deletions .github/workflows/codeql-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,12 @@ on:
- '.github/workflows/*.yml'
- 'tests/*'

permissions:
contents: read
security-events: write

jobs:
analyse:
name: Analyse
strategy:
fail-fast: false
matrix:
sdk: ["$NANOX_SDK", "$NANOSP_SDK", "$STAX_SDK", "$FLEX_SDK", "$APEX_P_SDK"]
# 'cpp' covers C and C++
language: ['cpp']
runs-on: ubuntu-latest
container:
image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest

steps:
- name: Clone
uses: actions/checkout@v4

- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
queries: security-and-quality

# CodeQL will create the database during the compilation
- name: Build
run: |
make BOLOS_SDK=${{ matrix.SDK }}

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
name: Call Ledger CodeQL analysis
uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_codeql_checks.yml@v1
secrets: inherit
20 changes: 7 additions & 13 deletions .github/workflows/codespell.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,13 @@ on:
- develop
pull_request:

permissions:
contents: read

jobs:
misspell:
name: Check misspellings
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true

- name: Check misspellings
uses: codespell-project/actions-codespell@v2
with:
builtin: clear,rare
check_filenames: true
ignore_words_list: ontop
skip: ./clients/wui/yarn.lock,
uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_spell_check.yml@v1
with:
ignore_words_list: ontop
skip_path: ./clients/wui/yarn.lock,
4 changes: 4 additions & 0 deletions .github/workflows/guidelines_enforcer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ on:
- develop
pull_request:

permissions:
contents: read
actions: write

jobs:
guidelines_enforcer:
name: Call Ledger guidelines_enforcer
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/lint-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ on:
- '.github/workflows/*.yml'
- 'tests/*'

permissions:
contents: read

jobs:
check_linting:
name: Check linting using the reusable workflow
Expand All @@ -20,7 +23,4 @@ jobs:

yamllint:
name: Check yaml files
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: yamllint .
uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_yaml_lint.yml@v1
59 changes: 7 additions & 52 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,57 +9,12 @@ on:
- develop
pull_request:

permissions:
contents: read
actions: write

jobs:
job_unit_test:
name: Unit test
runs-on: ubuntu-latest
container:
image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest

steps:
- name: Clone
uses: actions/checkout@v4

- name: Install cmocka
run: |
apt update && apt install -y libcmocka-dev lcov libbsd-dev

- name: Compile the tests
run: |
cd tests/unit/
rm -rf build/
cmake -B build -H.
make -C build

- name: Run the tests
run: |
cd tests/unit/
CTEST_OUTPUT_ON_FAILURE=1 make -C build test

- name: Generate code coverage
run: |
cd tests/unit/
lcov --directory . -b "$(realpath build/)" --capture --initial -o coverage.base
lcov --rc lcov_branch_coverage=1 --directory . -b "$(realpath build/)" --capture -o coverage.capture
lcov --directory . -b "$(realpath build/)" --add-tracefile coverage.base --add-tracefile coverage.capture -o coverage.info
lcov --directory . -b "$(realpath build/)" --remove coverage.info '*/tests/unit/*' -o coverage.info
genhtml coverage.info -o coverage

- uses: actions/upload-artifact@v4
with:
name: code-coverage
path: tests/unit/coverage

- name: Install codecov dependencies
run: apt install --no-install-recommends -y curl gpg

- name: Upload to codecov.io
uses: codecov/codecov-action@v5
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
files: ./tests/unit/coverage.info
flags: unittests
name: codecov-app-passwords
fail_ci_if_error: true
verbose: true
name: Call Ledger unit_test
uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_unit_tests.yml@v1
secrets: inherit
34 changes: 23 additions & 11 deletions .github/workflows/wui.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,35 @@ on:
- develop
- master

permissions:
contents: read

concurrency:
group: pages
cancel-in-progress: false

jobs:
generate:
name: Generate the site
runs-on: ubuntu-latest
defaults:
run:
working-directory: clients/wui/
steps:
- name: Clone
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Set Node.js 16.x
uses: actions/setup-node@v5
with:
node-version: 16.x
cache: yarn
cache-dependency-path: clients/wui/yarn.lock
- name: Run install
uses: borales/actions-yarn@v4
with:
cmd: install
dir: clients/wui/
run: yarn install --frozen-lockfile
- name: Run build
uses: borales/actions-yarn@v4
with:
cmd: build
dir: clients/wui/
run: yarn build
- name: Upload documentation bundle
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: site
path: clients/wui/build/
Expand All @@ -42,16 +48,22 @@ jobs:
name: Deploy the site on GitHub pages
runs-on: ubuntu-latest
needs: generate
permissions:
contents: write
if: |
github.event_name == 'push' &&
(github.ref == 'refs/heads/master' ||
github.ref == 'refs/heads/develop' ||
startsWith(github.ref, 'refs/tags/'))
steps:
- name: Download documentation bundle
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: site
path: site
- name: Deploy documentation on pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: site
cname: passwords.ledger.com
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.3.1] - 2026-06-01

### Fix

- Reject password creation when the chosen nickname is already in use.
- Hide actions that require existing passwords from the main menu when the
database is empty (only "Create a new password" is exposed in that case).
- Settings switches on the second page (separators, special characters, "press
enter") no longer display a stale value after toggling and re-entering the
settings.

### Change

- Reorder the main menu so "Create a new password" appears first.
- WUI deployment workflow: replace the third-party `borales/actions-yarn`
action with native `yarn` steps, enable yarn dependency caching via
`setup-node`, and set least-privilege `GITHUB_TOKEN` permissions.
- Pin the `passwords.ledger.com` custom domain in the deploy step (`cname`)
so it survives every GitHub Pages publish.

### Add

- Functional tests support Nano S+ / Nano X (Ragger button-based navigator).
- Unit tests for the new `nickname_exists` check.

## [1.3.0] - 2025-09-11

### Change
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ APPNAME ="Passwords"
# Application version
APPVERSION_M=1
APPVERSION_N=3
APPVERSION_P=0
APPVERSION_P=1
APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)

# Application source files
Expand Down
3 changes: 0 additions & 3 deletions pytest.ini

This file was deleted.

2 changes: 1 addition & 1 deletion src/metadata.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ uint32_t get_metadata(uint32_t nth) {
unsigned int offset = 0;
for (;;) {
if (METADATA_DATALEN(offset) == 0) {
return -1UL; // end of file
return UINT32_MAX; // end of file
}
if (METADATA_KIND(offset) != META_ERASED) {
if (nth == 0) {
Expand Down
21 changes: 21 additions & 0 deletions src/password.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,24 @@ void show_password_at_offset(const size_t offset, uint8_t* dest_buffer) {
error_type_t delete_password_at_offset(const size_t offset) {
return erase_metadata(offset);
}

bool nickname_exists(const char* const pwd_name, const size_t pwd_size) {
// write_metadata silently caps the total data block at MAX_METANAME bytes
// (1 charset byte + nickname), so any nickname longer than MAX_METANAME - 1
// is truncated at storage time. Mirror that truncation here, otherwise a
// user could bypass duplicate detection just by typing a name longer than
// what the device can actually persist.
const size_t effective_size =
(pwd_size > (size_t) (MAX_METANAME - 1)) ? (size_t) (MAX_METANAME - 1) : pwd_size;
for (size_t i = 0; i < N_storage.metadata_count; i++) {
uint32_t offset = get_metadata(i);
if (offset == UINT32_MAX) {
break;
}
if (METADATA_NICKNAME_LEN(offset) == effective_size &&
memcmp((const void*) METADATA_NICKNAME(offset), pwd_name, effective_size) == 0) {
return true;
}
}
return false;
}
2 changes: 2 additions & 0 deletions src/password.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "error.h"
Expand All @@ -14,3 +15,4 @@ error_type_t create_new_password(const char* const pwd_name, const size_t pwd_si
void type_password_at_offset(const size_t offset);
void show_password_at_offset(const size_t offset, uint8_t* dest_buffer);
error_type_t delete_password_at_offset(const size_t offset);
bool nickname_exists(const char* const pwd_name, const size_t pwd_size);
6 changes: 3 additions & 3 deletions src/password_list.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ void password_list_reset() {

size_t password_list_get_offset(const size_t index) {
if (index >= MAX_METADATA_COUNT) {
PRINTF("[password_list_get_offset] Index %d out of bounds\n", index);
PRINTF("[password_list_get_offset] Index %ld out of bounds\n", index);
return -1;
}
return passwordList.offsets[index];
Expand All @@ -57,7 +57,7 @@ size_t password_list_get_current_offset() {

const char *password_list_get_password(const size_t index) {
if (index >= MAX_METADATA_COUNT) {
PRINTF("[password_list_get_password] Index %d out of bounds\n", index);
PRINTF("[password_list_get_password] Index %ld out of bounds\n", index);
return NULL;
}
return passwordList.passwords[index];
Expand All @@ -72,7 +72,7 @@ bool password_list_add_password(const size_t index,
const char *const password,
const size_t length) {
if (index >= MAX_METADATA_COUNT) {
PRINTF("[password_list_add_password] Index %d out of bounds\n", index);
PRINTF("[password_list_add_password] Index %ld out of bounds\n", index);
return false;
}
passwordList.offsets[index] = offset;
Expand Down
Loading
Loading