From 7bd15d12adca5a36e6a56ecbd6a53dc06c5f7f4c Mon Sep 17 00:00:00 2001 From: Ciaran Concannon Date: Sat, 28 Jan 2023 19:45:01 +0000 Subject: [PATCH 01/22] Search for entries with no tags or stars with `-not -starred` and `-not -tagged` (#1663) * Allow for `-not -starred` to search for unstarred entries * Add `-tagged` and `-not -tagged` functionality --- jrnl/args.py | 59 ++++++++++++++++++++++++++++--- jrnl/controller.py | 9 +++++ jrnl/journals/Journal.py | 30 +++++++++------- tests/bdd/features/search.feature | 44 +++++++++++++++++++++++ tests/unit/test_parse_args.py | 3 ++ 5 files changed, 127 insertions(+), 18 deletions(-) diff --git a/jrnl/args.py b/jrnl/args.py index d23e1e53..b1055da3 100644 --- a/jrnl/args.py +++ b/jrnl/args.py @@ -28,6 +28,43 @@ class WrappingFormatter(argparse.RawTextHelpFormatter): return text +class IgnoreNoneAppendAction(argparse._AppendAction): + """ + Pass -not without a following string and avoid appending + a None value to the excluded list + """ + + def __call__(self, parser, namespace, values, option_string=None): + if values is not None: + super().__call__(parser, namespace, values, option_string) + + +def parse_not_arg( + args: list[str], parsed_args: argparse.Namespace, parser: argparse.ArgumentParser +) -> argparse.Namespace: + """ + It's possible to use -not as a precursor to -starred and -tagged + to reverse their behaviour, however this requires some extra logic + to parse, and to ensure we still do not allow passing an empty -not + """ + + parsed_args.exclude_starred = False + parsed_args.exclude_tagged = False + + if "-not-starred" in "".join(args): + parsed_args.starred = False + parsed_args.exclude_starred = True + if "-not-tagged" in "".join(args): + parsed_args.tagged = False + parsed_args.exclude_tagged = True + if "-not" in args and not any( + [parsed_args.exclude_starred, parsed_args.exclude_tagged, parsed_args.excluded] + ): + parser.error("argument -not: expected 1 argument") + + return parsed_args + + def parse_args(args: list[str] = []) -> argparse.Namespace: """ Argument parsing that is doable before the config is available. @@ -237,6 +274,12 @@ def parse_args(args: list[str] = []) -> argparse.Namespace: action="store_true", help="Show only starred entries (marked with *)", ) + reading.add_argument( + "-tagged", + dest="tagged", + action="store_true", + help="Show only entries that have at least one tag", + ) reading.add_argument( "-n", dest="limit", @@ -249,11 +292,15 @@ def parse_args(args: list[str] = []) -> argparse.Namespace: reading.add_argument( "-not", dest="excluded", - nargs=1, + nargs="?", default=[], - metavar="TAG", - action="extend", - help="Exclude entries with this tag", + metavar="TAG/FLAG", + action=IgnoreNoneAppendAction, + help=( + "If passed a string, will exclude entries with that tag. " + "Can be also used before -starred or -tagged flags, to exclude " + "starred or tagged entries respectively." + ), ) search_options_msg = """ These help you do various tasks with the selected entries from your search. @@ -388,5 +435,7 @@ def parse_args(args: list[str] = []) -> argparse.Namespace: # Handle '-123' as a shortcut for '-n 123' num = re.compile(r"^-(\d+)$") args = [num.sub(r"-n \1", arg) for arg in args] + parsed_args = parser.parse_intermixed_args(args) + parsed_args = parse_not_arg(args, parsed_args, parser) - return parser.parse_intermixed_args(args) + return parsed_args diff --git a/jrnl/controller.py b/jrnl/controller.py index 7d7c87ce..7a0a4965 100644 --- a/jrnl/controller.py +++ b/jrnl/controller.py @@ -89,6 +89,8 @@ def _is_write_mode(args: "Namespace", config: dict, **kwargs) -> bool: args.edit, args.change_time, args.excluded, + args.exclude_starred, + args.exclude_tagged, args.export, args.end_date, args.today_in_history, @@ -101,6 +103,7 @@ def _is_write_mode(args: "Namespace", config: dict, **kwargs) -> bool: args.starred, args.start_date, args.strict, + args.tagged, args.tags, ) ) @@ -270,7 +273,10 @@ def _has_search_args(args: "Namespace") -> bool: args.end_date, args.strict, args.starred, + args.tagged, args.excluded, + args.exclude_starred, + args.exclude_tagged, args.contains, args.limit, ) @@ -296,7 +302,10 @@ def _filter_journal_entries(args: "Namespace", journal: Journal, **kwargs) -> No end_date=args.end_date, strict=args.strict, starred=args.starred, + tagged=args.tagged, exclude=args.excluded, + exclude_starred=args.exclude_starred, + exclude_tagged=args.exclude_tagged, contains=args.contains, ) journal.limit(args.limit) diff --git a/jrnl/journals/Journal.py b/jrnl/journals/Journal.py index 5a8b016e..994e0098 100644 --- a/jrnl/journals/Journal.py +++ b/jrnl/journals/Journal.py @@ -229,16 +229,19 @@ class Journal: def filter( self, - tags: list = [], - month: str | int | None = None, - day: str | int | None = None, - year: str | None = None, - start_date: str | None = None, - end_date: str | None = None, - starred: bool = False, - strict: bool = False, - contains: bool = None, - exclude: list = [], + tags=[], + month=None, + day=None, + year=None, + start_date=None, + end_date=None, + starred=False, + tagged=False, + exclude_starred=False, + exclude_tagged=False, + strict=False, + contains=None, + exclude=[], ): """Removes all entries from the journal that don't match the filter. @@ -259,7 +262,7 @@ class Journal: start_date = time.parse(start_date) # If strict mode is on, all tags have to be present in entry - tagged = self.search_tags.issubset if strict else self.search_tags.intersection + has_tags = self.search_tags.issubset if strict else self.search_tags.intersection def excluded(tags): return 0 < len([tag for tag in tags if tag in excluded_tags]) @@ -275,8 +278,9 @@ class Journal: result = [ entry for entry in self.entries - if (not tags or tagged(entry.tags)) - and (not starred or entry.starred) + if (not tags or has_tags(entry.tags)) + and (not (starred or exclude_starred) or entry.starred == starred) + and (not (tagged or exclude_tagged) or bool(entry.tags) == tagged) and (not month or entry.date.month == compare_d.month) and (not day or entry.date.day == compare_d.day) and (not year or entry.date.year == compare_d.year) diff --git a/tests/bdd/features/search.feature b/tests/bdd/features/search.feature index d653a621..138c0285 100644 --- a/tests/bdd/features/search.feature +++ b/tests/bdd/features/search.feature @@ -124,6 +124,50 @@ Feature: Searching in a journal | basic_folder.yaml | | basic_dayone.yaml | + + Scenario: Searching for unstarred entries + Given we use the config "" + And we use the password "test" if prompted + When we run "jrnl -not -starred" + Then we should get no error + And the output should contain "2 entries found" + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario: Searching for tagged entries + Given we use the config "" + And we use the password "test" if prompted + When we run "jrnl -tagged" + Then we should get no error + And the output should contain "3 entries found" + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario: Searching for untagged entries + Given we use the config "empty_folder.yaml" + When we run "jrnl Tagged entry. This one has a @tag." + Then we should get no error + When we run "jrnl Untagged entry. This one has no tag." + Then we should get no error + When we run "jrnl -not -tagged" + Then we should get no error + And the output should contain "1 entry found" + And the output should contain "This one has no tag" + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + Scenario Outline: Searching for dates Given we use the config "" When we run "jrnl -on 2020-08-31 --short" diff --git a/tests/unit/test_parse_args.py b/tests/unit/test_parse_args.py index ccb7f5a2..0b266d23 100644 --- a/tests/unit/test_parse_args.py +++ b/tests/unit/test_parse_args.py @@ -23,6 +23,8 @@ def expected_args(**kwargs): "change_time": None, "edit": False, "end_date": None, + "exclude_starred": False, + "exclude_tagged": False, "today_in_history": False, "month": None, "day": None, @@ -38,6 +40,7 @@ def expected_args(**kwargs): "starred": False, "start_date": None, "strict": False, + "tagged": False, "tags": False, "text": [], "config_override": [], From d4ce2a84cfbea665e5249f06d6112dd9cc661637 Mon Sep 17 00:00:00 2001 From: Jrnl Bot Date: Sat, 28 Jan 2023 19:47:17 +0000 Subject: [PATCH 02/22] Update changelog [ci skip] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dff0f843..ad10e106 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ **Implemented enhancements:** - Don't import cryptography package if not needed [\#1521](https://github.com/jrnl-org/jrnl/issues/1521) +- Search for entries with no tags or stars with `-not -starred` and `-not -tagged` [\#1663](https://github.com/jrnl-org/jrnl/pull/1663) ([cjcon90](https://github.com/cjcon90)) - Refactor flow for easier access to some files \(avoid things like `jrnl.Journal.Journal` and `jrnl.jrnl` co-existing\) [\#1662](https://github.com/jrnl-org/jrnl/pull/1662) ([wren](https://github.com/wren)) - Add more type hints [\#1642](https://github.com/jrnl-org/jrnl/pull/1642) ([outa](https://github.com/outa)) - Add `rich` handler to debug logging [\#1627](https://github.com/jrnl-org/jrnl/pull/1627) ([wren](https://github.com/wren)) From 2d1a52afe51b3c2dbac97d658dd63fe439010d66 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Andrea Date: Sat, 11 Feb 2023 21:10:18 +0100 Subject: [PATCH 03/22] Update documentation on temporary files naming (#1673) --- docs/privacy-and-security.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/privacy-and-security.md b/docs/privacy-and-security.md index a55e6fa3..32cf8ac3 100644 --- a/docs/privacy-and-security.md +++ b/docs/privacy-and-security.md @@ -78,7 +78,7 @@ unencrypted temporary remains on your disk. If your computer were to shut off during this time, or the `jrnl` process were killed unexpectedly, then the unencrypted temporary file will remain on your disk. You can mitigate this issue by only saving with your editor right before closing it. You can also -manually delete these files (i.e. files named `jrnl_*.txt`) from your temporary +manually delete these files (i.e. files named `jrnl*.jrnl`) from your temporary folder. ## Plausible deniability From 6b179e673b0637fcc9ce1b6c9917aa34cd8f36f1 Mon Sep 17 00:00:00 2001 From: Jrnl Bot Date: Sat, 11 Feb 2023 20:12:27 +0000 Subject: [PATCH 04/22] Update changelog [ci skip] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad10e106..1abc2e78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ **Documentation:** - Documentation Change [\#1651](https://github.com/jrnl-org/jrnl/issues/1651) +- Update documentation on temporary files naming [\#1673](https://github.com/jrnl-org/jrnl/pull/1673) ([giuseppedandrea](https://github.com/giuseppedandrea)) - Update docs to include time and title in arguments with `--edit` [\#1657](https://github.com/jrnl-org/jrnl/pull/1657) ([pconrad-fb](https://github.com/pconrad-fb)) - Fix markup in "Advanced Usage" doc [\#1655](https://github.com/jrnl-org/jrnl/pull/1655) ([multani](https://github.com/multani)) - Remove Windows 7 known issue since Windows 7 is no longer supported [\#1636](https://github.com/jrnl-org/jrnl/pull/1636) ([micahellison](https://github.com/micahellison)) From 6c6937c5078fa4a7a31d18e044cc067a74dd7013 Mon Sep 17 00:00:00 2001 From: David Isaksson Date: Sat, 11 Feb 2023 21:16:31 +0100 Subject: [PATCH 05/22] Add documentation about information leaks in Vim/Neovim (#1674) * Add documentation about using Vim/Neovim as editor * Add documentation about information leaks in editors * Spelling fix --------- Co-authored-by: Jonathan Wren --- docs/external-editors.md | 14 +++++++ docs/privacy-and-security.md | 78 ++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/docs/external-editors.md b/docs/external-editors.md index 977a9de2..b7219d8d 100644 --- a/docs/external-editors.md +++ b/docs/external-editors.md @@ -37,6 +37,9 @@ jrnl yesterday: All my troubles seemed so far away. --edit All editors must be [blocking processes](https://en.wikipedia.org/wiki/Blocking_(computing)) to work with jrnl. Some editors, such as [micro](https://micro-editor.github.io/), are blocking by default, though others can be made to block with additional arguments, such as many of those documented below. If jrnl opens your editor but finishes running immediately, then your editor is not a blocking process, and you may be able to correct that with one of the suggestions below. +Please see [this section](./privacy-and-security.md#editor-history) about how +your editor might leak sensitive information and how to mitigate that risk. + ## Sublime Text To use [Sublime Text](https://www.sublimetext.com/), install the command line @@ -71,6 +74,17 @@ back to journal. In the case of MacVim, this is `-f`: editor: "mvim -f" ``` +## Vim/Neovim + +To use any of the Vim derivatives as editor in Linux, simply set the `editor` +to the executable: + +```yaml +editor: "vim" +# or +editor: "nvim" +``` + ## iA Writer On OS X, you can use the fabulous [iA diff --git a/docs/privacy-and-security.md b/docs/privacy-and-security.md index 32cf8ac3..c767c5e0 100644 --- a/docs/privacy-and-security.md +++ b/docs/privacy-and-security.md @@ -67,6 +67,84 @@ Windows doesn't log history to disk, but it does keep it in your command prompt session. Close the command prompt or press `Alt`+`F7` to clear your history after journaling. +## Editor history + +Some editors keep usage history stored on disk for future use. This can be a +security risk in the sense that sensitive information can leak via recent +search patterns or editor commands. + +### Vim + +Vim stores progress data in a so called Viminfo file located at `~/.viminfo` +which contains all sorts of user data including command line history, search +string history, search/substitute patterns, contents of register etc. Also to +be able to recover opened files after an unexpected application close Vim uses +swap files. + +These options as well as other leaky features can be disabled by setting the +`editor` key in the Jrnl settings like this: + +``` yaml +editor: "vim -c 'set viminfo= noswapfile noundofile nobackup nowritebackup noshelltemp history=0 nomodeline secure'" +``` + +To disable all plugins and custom configurations and start Vim with the default +configuration `-u NONE` can be passed on the command line as well. This will +ensure that any rogue plugins or other difficult to catch information leaks are +eliminated. The downside to this is that the editor experience will decrease +quite a bit. + +To instead let Vim automatically detect when a Jrnl file is being edited an +autocommand can be used. Place this in your `~/.vimrc`: + +``` vim +autocmd BufNewFile,BufReadPre *.jrnl setlocal viminfo= noswapfile noundofile nobackup nowritebackup noshelltemp history=0 nomodeline secure +``` + +Please see `:h