🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

granit-parser

Package Overview
Dependencies
Maintainers
0
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

granit-parser - cargo Package Compare versions

Comparing version
0.0.4
to
0.0.5
+49
.github/workflows/fuzz.yml
name: Fuzz
on:
pull_request:
push:
branches:
- master
schedule:
- cron: "30 3 * * 0"
workflow_dispatch:
permissions:
contents: read
concurrency:
group: fuzz-${{ github.ref }}
cancel-in-progress: true
jobs:
fuzz:
name: Build and smoke-run fuzz targets
runs-on: ubuntu-latest
timeout-minutes: 30
env:
CARGO_TERM_COLOR: always
FUZZ_SECONDS: ${{ github.event_name == 'schedule' && '1200' || '20' }}
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Install nightly Rust
run: rustup toolchain install nightly --profile minimal --no-self-update
- name: Rust cache
uses: Swatinem/rust-cache@v2
- name: Install cargo-fuzz
run: cargo +nightly install cargo-fuzz
- name: Build fuzz targets
run: cargo +nightly fuzz build
- name: Smoke-run fuzz targets
shell: bash
run: |
set -euo pipefail
for target in aliases_merges duplicate_keys flow_collections large_scalars; do
cargo +nightly fuzz run "${target}" -- -max_total_time="${FUZZ_SECONDS}" -timeout=10 -rss_limit_mb=4096
done
use std::panic::{catch_unwind, AssertUnwindSafe};
use granit_parser::Parser;
const CRASH_CA24C2F5B1341124FCD324CBAAFCAA9A1F6D034C: &[u8] = &[
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 10, 9, 35, 8, 10, 9, 255, 255, 255, 255,
255, 10, 9, 35, 8, 35, 91, 93, 58,
];
fn drain_str_parser(input: &str) {
for event in Parser::new_from_str(input) {
if event.is_err() {
break;
}
}
}
fn drain_iter_parser(input: &str) {
for event in Parser::new_from_iter(input.chars()) {
if event.is_err() {
break;
}
}
}
#[test]
fn crash_ca24c2f5b1341124fcd324cbaafcaa9a1f6d034c_does_not_panic() {
let s = String::from_utf8_lossy(CRASH_CA24C2F5B1341124FCD324CBAAFCAA9A1F6D034C);
let inputs = [
format!("[{s}]"),
format!("{{{s}}}"),
format!("root: {{{s}}}\narray: [{s}]\n"),
];
for input in inputs {
let result = catch_unwind(AssertUnwindSafe(|| drain_str_parser(&input)));
assert!(result.is_ok(), "str parser panicked for input: {input:?}");
let result = catch_unwind(AssertUnwindSafe(|| drain_iter_parser(&input)));
assert!(result.is_ok(), "iter parser panicked for input: {input:?}");
}
}
+1
-1
{
"git": {
"sha1": "ca296b7858b3fbd531794e7ecda5732f0bd8206f"
"sha1": "9359b0a65b0968c6c69a77138d4d007068f871f9"
},
"path_in_vcs": ""
}

@@ -115,6 +115,5 @@ # This file is automatically @generated by Cargo.

name = "granit-parser"
version = "0.0.4"
version = "0.0.5"
dependencies = [
"arraydeque",
"clap",
"libtest-mimic",

@@ -121,0 +120,0 @@ "smallvec",

@@ -16,3 +16,3 @@ # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO

name = "granit-parser"
version = "0.0.4"
version = "0.0.5"
authors = [

@@ -119,2 +119,6 @@ "Ethiraric <ethiraric@gmail.com>",

[[test]]
name = "flow_collections_fuzz_regression"
path = "tests/flow_collections_fuzz_regression.rs"
[[test]]
name = "fuzz"

@@ -191,5 +195,2 @@ path = "tests/fuzz.rs"

[dev-dependencies.clap]
version = "=4.5.48"
[dev-dependencies.libtest-mimic]

@@ -196,0 +197,0 @@ version = "=0.8.1"

# Changelog
## v0.0.4
## v0.0.5
- Performance improvements on comment parsing
- Added `Span::tag_start` metadata for parser-emitted tagged node events, so diagnostics can point
at the explicit tag token even when the node span starts on a later line.
- Added `Tag::original_handle`, `Tag::original_parts`, and `Tag::original` so consumers can inspect
the tag handle as written before `%TAG` directive resolution and reconstruct normalized
author-facing tag spelling, including verbatim tags (breaking change).
## v0.0.4
Retracted

@@ -6,0 +13,0 @@ ## v0.0.3

+61
-36

@@ -6,2 +6,3 @@ # granit-parser

[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/bourumir-wyngs/granit-parser/ci.yml)](https://github.com/bourumir-wyngs/granit-parser/actions/workflows/ci.yml)
[![Fuzz](https://github.com/bourumir-wyngs/granit-parser/actions/workflows/fuzz.yml/badge.svg)](https://github.com/bourumir-wyngs/granit-parser/actions/workflows/fuzz.yml)
[![docs.rs](https://docs.rs/granit-parser/badge.svg)](https://docs.rs/granit-parser)

@@ -37,3 +38,3 @@ [![codecov](https://codecov.io/gh/bourumir-wyngs/granit-parser/graph/badge.svg)](https://codecov.io/gh/bourumir-wyngs/granit-parser)

[`Parser::new_from_str`](https://docs.rs/granit-parser/latest/granit_parser/struct.Parser.html#method.new_from_str) returns an iterator of ([`Event`](https://docs.rs/granit-parser/latest/granit_parser/enum.Event.html), [`Span`](https://docs.rs/granit-parser/latest/granit_parser/struct.Span.html)) pairs. The event helpers expose common node metadata, and spans provide byte ranges plus source slices:
[`Parser::new_from_str`](https://docs.rs/granit-parser/latest/granit_parser/struct.Parser.html#method.new_from_str) returns an iterator of ([`Event`](https://docs.rs/granit-parser/latest/granit_parser/enum.Event.html), [`Span`](https://docs.rs/granit-parser/latest/granit_parser/struct.Span.html)) pairs. The event helpers expose common node metadata, and spans provide byte ranges, source slices, and explicit tag-token starts for tagged nodes:

@@ -47,4 +48,7 @@ Comments are emitted as `Event::Comment(text, placement)`. They are presentation metadata for tools such as linters and formatters, not YAML data nodes, so consumers that build YAML values should filter them out. The companion `Span` for a comment covers the whole source comment, including `#` and excluding the line break; when parsing from `Parser::new_from_str`, `span.slice(yaml)` returns that source comment text.

let yaml = r#"
%TAG !example! tag:example.com,2000:
---
items: !shopping
- milk
- !example!sliced bread
- !!str bread

@@ -63,9 +67,16 @@ locations: # Example with composite keys

if let Some(tag) = event.tag() {
let tag_start = span
.tag_start()
.map(|mark| (mark.line(), mark.col(), mark.byte_offset()));
if let Some((value, _style)) = event.scalar() {
println!(
"scalar tag: {tag} core-str={} for {value:?}",
"scalar tag: {tag} core-str={} tag_start(line,col,byte)={tag_start:?} for {value:?}",
tag.is_yaml_core_schema_tag("str")
);
} else if event.is_node() {
println!("node tag: {tag} custom={}", tag.is_custom());
println!(
"node tag: {tag} custom={} tag_start(line,col,byte)={tag_start:?}",
tag.is_custom()
);
}

@@ -89,31 +100,33 @@ }

StreamStart bytes=Some(0..0) source=Some("")
DocumentStart(false) bytes=Some(1..1) source=Some("")
MappingStart(Block, 0, None) bytes=Some(1..1) source=Some("")
Scalar("items", Plain, 0, None) bytes=Some(1..6) source=Some("items")
node tag: !shopping custom=true
SequenceStart(Block, 0, Some(Tag { handle: "!", suffix: "shopping" })) bytes=Some(20..20) source=Some("")
Scalar("milk", Plain, 0, None) bytes=Some(22..26) source=Some("milk")
scalar tag: tag:yaml.org,2002:str core-str=true for "bread"
Scalar("bread", Plain, 0, Some(Tag { handle: "tag:yaml.org,2002:", suffix: "str" })) bytes=Some(37..42) source=Some("bread")
SequenceEnd bytes=Some(43..43) source=Some("")
Scalar("locations", Plain, 0, None) bytes=Some(43..52) source=Some("locations")
Comment(" Example with composite keys", Right) bytes=Some(54..83) source=Some("# Example with composite keys")
MappingStart(Block, 0, None) bytes=Some(86..86) source=Some("")
SequenceStart(Flow, 0, None) bytes=Some(86..87) source=Some("[")
Scalar("47.3769", Plain, 0, None) bytes=Some(87..94) source=Some("47.3769")
Scalar("8.5417", Plain, 0, None) bytes=Some(96..102) source=Some("8.5417")
SequenceEnd bytes=Some(102..103) source=Some("]")
Scalar("local", Plain, 0, None) bytes=Some(105..110) source=Some("local")
SequenceStart(Flow, 0, None) bytes=Some(113..114) source=Some("[")
Scalar("40.7128", Plain, 0, None) bytes=Some(114..121) source=Some("40.7128")
Scalar("-74.0060", Plain, 0, None) bytes=Some(123..131) source=Some("-74.0060")
SequenceEnd bytes=Some(131..132) source=Some("]")
Scalar("remote", Plain, 0, None) bytes=Some(134..140) source=Some("remote")
Comment(" JSON-style \\uXXXX surrogate pairs:", Above) bytes=Some(142..178) source=Some("# JSON-style \\uXXXX surrogate pairs:")
MappingEnd bytes=Some(179..179) source=Some("")
Scalar("music", Plain, 0, None) bytes=Some(179..184) source=Some("music")
Scalar("𝄞🎵🎶", DoubleQuoted, 0, None) bytes=Some(186..224) source=Some("\"\\uD834\\uDD1E\\uD83C\\uDFB5\\uD83C\\uDFB6\"")
MappingEnd bytes=Some(225..225) source=Some("")
DocumentEnd bytes=Some(225..225) source=Some("")
StreamEnd bytes=Some(225..225) source=Some("")
DocumentStart(true) bytes=Some(38..41) source=Some("---")
MappingStart(Block, 0, None) bytes=Some(42..42) source=Some("")
Scalar("items", Plain, 0, None) bytes=Some(42..47) source=Some("items")
node tag: !shopping custom=true tag_start(line,col,byte)=Some((4, 7, Some(49)))
SequenceStart(Block, 0, Some(Tag { handle: "!", suffix: "shopping", original_handle: "!" })) bytes=Some(61..61) source=Some("")
Scalar("milk", Plain, 0, None) bytes=Some(63..67) source=Some("milk")
scalar tag: tag:example.com,2000:sliced core-str=false tag_start(line,col,byte)=Some((6, 4, Some(72))) for "bread"
Scalar("bread", Plain, 0, Some(Tag { handle: "tag:example.com,2000:", suffix: "sliced", original_handle: "!example!" })) bytes=Some(88..93) source=Some("bread")
scalar tag: tag:yaml.org,2002:str core-str=true tag_start(line,col,byte)=Some((7, 4, Some(98))) for "bread"
Scalar("bread", Plain, 0, Some(Tag { handle: "tag:yaml.org,2002:", suffix: "str", original_handle: "!!" })) bytes=Some(104..109) source=Some("bread")
SequenceEnd bytes=Some(110..110) source=Some("")
Scalar("locations", Plain, 0, None) bytes=Some(110..119) source=Some("locations")
Comment(" Example with composite keys", Right) bytes=Some(121..150) source=Some("# Example with composite keys")
MappingStart(Block, 0, None) bytes=Some(153..153) source=Some("")
SequenceStart(Flow, 0, None) bytes=Some(153..154) source=Some("[")
Scalar("47.3769", Plain, 0, None) bytes=Some(154..161) source=Some("47.3769")
Scalar("8.5417", Plain, 0, None) bytes=Some(163..169) source=Some("8.5417")
SequenceEnd bytes=Some(169..170) source=Some("]")
Scalar("local", Plain, 0, None) bytes=Some(172..177) source=Some("local")
SequenceStart(Flow, 0, None) bytes=Some(180..181) source=Some("[")
Scalar("40.7128", Plain, 0, None) bytes=Some(181..188) source=Some("40.7128")
Scalar("-74.0060", Plain, 0, None) bytes=Some(190..198) source=Some("-74.0060")
SequenceEnd bytes=Some(198..199) source=Some("]")
Scalar("remote", Plain, 0, None) bytes=Some(201..207) source=Some("remote")
Comment(" JSON-style \\uXXXX surrogate pairs:", Above) bytes=Some(209..245) source=Some("# JSON-style \\uXXXX surrogate pairs:")
MappingEnd bytes=Some(246..246) source=Some("")
Scalar("music", Plain, 0, None) bytes=Some(246..251) source=Some("music")
Scalar("𝄞🎵🎶", DoubleQuoted, 0, None) bytes=Some(253..291) source=Some("\"\\uD834\\uDD1E\\uD83C\\uDFB5\\uD83C\\uDFB6\"")
MappingEnd bytes=Some(292..292) source=Some("")
DocumentEnd bytes=Some(292..292) source=Some("")
StreamEnd bytes=Some(292..292) source=Some("")
```

@@ -233,7 +246,19 @@

### Improved ergonomics
Release 0.0.3 includes ergonomic helpers such as `Event::tag`, `Event::scalar`,
`Event::anchor_id`, `Event::alias_id`, `Event::is_node`, `Tag::parts`,
`Tag::is_custom`, `Tag::is_yaml_core_schema_tag`, `Span::slice`, and
`ParserStack::push_include`. See CHANGELOG.md for details.
The following ergonomic helpers are available:
- `Event::tag`
- `Event::scalar`
- `Event::anchor_id`
- `Event::alias_id`
- `Event::is_node`
- `Tag::parts`
- `Tag::original_parts`
- `Tag::original`
- `Tag::is_custom`
- `Tag::is_yaml_core_schema_tag`
- `Span::slice`
- `Span::tag_start`
- `ParserStack::push_include`
See CHANGELOG.md for details.
## Tools

@@ -240,0 +265,0 @@

@@ -33,9 +33,16 @@ use granit_parser::{Parser, ScanError};

if let Some(tag) = event.tag() {
let tag_start = span
.tag_start()
.map(|mark| (mark.line(), mark.col(), mark.byte_offset()));
if let Some((value, _style)) = event.scalar() {
lines.push(format!(
"scalar tag: {tag} core-str={} for {value:?}",
"scalar tag: {tag} core-str={} tag_start(line,col,byte)={tag_start:?} for {value:?}",
tag.is_yaml_core_schema_tag("str")
));
} else if event.is_node() {
lines.push(format!("node tag: {tag} custom={}", tag.is_custom()));
lines.push(format!(
"node tag: {tag} custom={} tag_start(line,col,byte)={tag_start:?}",
tag.is_custom()
));
}

@@ -42,0 +49,0 @@ }

@@ -75,2 +75,3 @@ #![allow(clippy::bool_assert_comparison)]

assert_eq!(span.byte_range(), Some(5..13));
assert_eq!(span.tag_start(), None);

@@ -86,2 +87,69 @@ let empty = granit_parser::Span::empty(Marker::new(6, 1, 6).with_byte_offset(Some(13)));

#[test]
fn tagged_block_collection_reports_tag_start_on_tag_line() {
let yaml = "key: !!omap # tag is here\n a: 1\n";
let (_event, span) = Parser::new_from_str(yaml)
.map(Result::unwrap)
.find(|(event, _span)| {
matches!(
event,
Event::MappingStart(_, _, Some(tag)) if tag.suffix == "omap"
)
})
.expect("expected tagged block mapping");
assert_eq!(span.start.line(), 2);
let tag_start = span
.tag_start()
.expect("tagged node should report tag start");
assert_eq!(tag_start.line(), 1);
assert_eq!(tag_start.col(), 5);
assert_eq!(tag_start.byte_offset(), Some(5));
}
#[test]
fn tag_start_uses_tag_token_when_anchor_precedes_tag() {
let yaml = "key: &a !!str value\n";
let (_event, span) = Parser::new_from_str(yaml)
.map(Result::unwrap)
.find(|(event, _span)| {
matches!(
event,
Event::Scalar(value, _, _, Some(tag))
if value.as_ref() == "value" && tag.suffix == "str"
)
})
.expect("expected tagged anchored scalar");
let tag_start = span
.tag_start()
.expect("tagged node should report tag start");
assert_eq!(tag_start.line(), 1);
assert_eq!(tag_start.col(), 8);
assert_eq!(tag_start.byte_offset(), Some(8));
}
#[test]
fn tag_start_uses_tag_token_when_tag_precedes_anchor() {
let yaml = "key: !!str &a value\n";
let (_event, span) = Parser::new_from_str(yaml)
.map(Result::unwrap)
.find(|(event, _span)| {
matches!(
event,
Event::Scalar(value, _, _, Some(tag))
if value.as_ref() == "value" && tag.suffix == "str"
)
})
.expect("expected tagged anchored scalar");
let tag_start = span
.tag_start()
.expect("tagged node should report tag start");
assert_eq!(tag_start.line(), 1);
assert_eq!(tag_start.col(), 5);
assert_eq!(tag_start.byte_offset(), Some(5));
}
#[test]
fn span_slice_returns_source_text_for_valid_byte_ranges() {

@@ -88,0 +156,0 @@ let source = "key: value";

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display