Repository navigation
Extract lockr app IDs from audit page metadata #1496
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
0eda2fb
cc11942
a0a8d13
f1b5d4a
41dcf1f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,8 @@ use crate::commands::audit::generate::{ | |
| }; | ||
| use crate::error::{CliResult, report_error}; | ||
|
|
||
| const LOCKR_APP_ID_SELECTOR: &str = "meta[name=\"lockr-signin-app_id\"]"; | ||
|
|
||
| static GTM_REGEX: LazyLock<Regex> = | ||
| LazyLock::new(|| Regex::new(r"\bGTM-[A-Z0-9]{4,20}\b").expect("should compile GTM regex")); | ||
| static GPT_INLINE_REGEX: LazyLock<Regex> = LazyLock::new(|| { | ||
|
|
@@ -65,6 +67,26 @@ pub(crate) fn analyze_collected_page(collected: &CollectedPage) -> CliResult<Aud | |
| )); | ||
| } | ||
|
|
||
| let lockr_selector = | ||
| Selector::parse(LOCKR_APP_ID_SELECTOR).expect("should parse lockr app ID selector"); | ||
| let mut lockr_metadata = document.select(&lockr_selector).peekable(); | ||
| if lockr_metadata.peek().is_some() { | ||
|
ChristianPavilonis marked this conversation as resolved.
Resolved
|
||
| integrations.insert("lockr".to_string(), LOCKR_APP_ID_SELECTOR.to_string()); | ||
| } | ||
| let mut lockr_app_ids = lockr_metadata | ||
| .filter_map(|element| element.value().attr("content")) | ||
| .map(str::trim) | ||
| .filter(|value| !value.is_empty()); | ||
| let lockr_app_id = lockr_app_ids.next().map(ToOwned::to_owned); | ||
| if let Some(first) = &lockr_app_id | ||
| && lockr_app_ids.any(|other| other != first.as_str()) | ||
| { | ||
| warnings.push( | ||
| "page carries more than one distinct lockr app ID; the draft uses the first" | ||
| .to_string(), | ||
| ); | ||
|
Comment on lines
+84
to
+87
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔧 wrench — This warning now leaks into Repro at "the draft uses the first" is wrong for that command, which only rewrites Proposed fix (apply manually; it touches
/// Recorded when page metadata carries more than one distinct lockr app ID.
pub(super) const LOCKR_APP_ID_CONFLICT_WARNING: &str =
"page carries more than one distinct lockr app ID; the draft uses the first";
warnings.push(LOCKR_APP_ID_CONFLICT_WARNING.to_string());
for warning in &artifact.warnings {
// Lockr app IDs only feed the `ts audit generate` draft. Slot generation
// writes no lockr settings, so the conflict is not actionable here.
if warning == analyzer::LOCKR_APP_ID_CONFLICT_WARNING {
continue;
}Regression test for the #[test]
fn update_slots_omits_lockr_app_id_conflicts_from_notes() {
let temp = TempDir::new().expect("should create temp dir");
let config_path = temp.path().join("trusted-server.toml");
fs::write(
&config_path,
"[creative_opportunities]\ngam_network_id = \"123456789\"\n",
)
.expect("should write config");
let mut page = collected_page_with_header_slot();
page.html = r#"<html><head><meta name="lockr-signin-app_id" content="first-app-id"><meta name="lockr-signin-app_id" content="second-app-id"></head></html>"#.to_string();
let collector = FakeCollector::new(page);
let mut err = Vec::new();
run_update_slots(
&UpdateSlotsRequest {
url: "https://publisher.example/",
config_path: &config_path,
existing_creative: None,
page_patterns: &[],
replace: false,
cookies: &[],
dry_run: true,
yes: false,
interactive: false,
scroll: false,
budget: CrawlBudget::default(),
},
&[("desktop", &collector)],
&mut Vec::new(),
&mut err,
&mut io::empty(),
)
.expect("should preview generated slots");
let notes = String::from_utf8(err).expect("should write UTF-8 notes");
assert!(
!notes.contains("lockr app ID"),
"should not report a generate-only lockr conflict during slot generation: {notes}"
);
}Scratch-verified at |
||
| } | ||
|
|
||
| for tag in &collected.script_tags { | ||
| if let Some(src) = &tag.src { | ||
| if let Ok(asset_url) = final_url.join(src) { | ||
|
|
@@ -113,6 +135,7 @@ pub(crate) fn analyze_collected_page(collected: &CollectedPage) -> CliResult<Aud | |
| .or(derived_title); | ||
|
|
||
| Ok(AuditArtifact { | ||
| lockr_app_id, | ||
| audited_url: final_url.to_string(), | ||
| page_title, | ||
| js_asset_count: assets.len(), | ||
|
|
@@ -315,6 +338,46 @@ mod tests { | |
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn analyze_collected_page_warns_only_on_distinct_lockr_app_ids() { | ||
| for (other_id, conflicting) in [(" first-app-id ", false), ("second-app-id", true)] { | ||
| let collected = CollectedPage { | ||
| requested_url: "https://publisher.example/page".to_string(), | ||
| final_url: "https://publisher.example/page".to_string(), | ||
| page_title: None, | ||
| html: format!( | ||
| r#"<meta name="lockr-signin-app_id"><meta name="lockr-signin-app_id" content=" "><meta name="lockr-signin-app_id" content=" first-app-id "><meta name="lockr-signin-app_id" content="{other_id}"><meta name="lockr-signin-app_id" content="{other_id}">"# | ||
| ), | ||
| script_tags: Vec::new(), | ||
| network_requests: Vec::new(), | ||
| gpt_slots: Vec::new(), | ||
| links: Vec::new(), | ||
| sitemap_locs: Vec::new(), | ||
| warnings: vec!["existing warning".to_string()], | ||
| }; | ||
|
|
||
| let artifact = | ||
| analyze_collected_page(&collected).expect("should analyze collected page"); | ||
|
|
||
| assert_eq!( | ||
| artifact.lockr_app_id.as_deref(), | ||
| Some("first-app-id"), | ||
| "should retain the first nonempty normalized ID" | ||
| ); | ||
| let mut expected_warnings = vec!["existing warning".to_string()]; | ||
| if conflicting { | ||
| expected_warnings.push( | ||
| "page carries more than one distinct lockr app ID; the draft uses the first" | ||
| .to_string(), | ||
| ); | ||
| } | ||
| assert_eq!( | ||
| artifact.warnings, expected_warnings, | ||
| "should preserve existing warnings and warn once only for distinct IDs without echoing them" | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn analyze_collected_page_uses_html_title_when_browser_title_absent() { | ||
| let collected = CollectedPage { | ||
|
|
@@ -559,6 +622,7 @@ mod tests { | |
| "https://tags.example.com/gtm.js?id=GTM-ABC123&l=dataLayer", | ||
| ] { | ||
| let artifact = AuditArtifact { | ||
| lockr_app_id: None, | ||
| audited_url: "https://example.com".to_string(), | ||
| page_title: None, | ||
| js_asset_count: 0, | ||
|
|
@@ -600,6 +664,7 @@ mod tests { | |
|
|
||
| let evidence = format!("https://tags.example.com/gtm.js?id={container_id}&l=dataLayer"); | ||
| let mut artifact = AuditArtifact { | ||
| lockr_app_id: None, | ||
| audited_url: "https://publisher.example.com".to_string(), | ||
| page_title: None, | ||
| js_asset_count: 0, | ||
|
|
@@ -648,6 +713,7 @@ mod tests { | |
| ("https://tags.example.com/gtm.js?id=GTM-ABC", "GTM-BBB222"), | ||
| ] { | ||
| let artifact = AuditArtifact { | ||
| lockr_app_id: None, | ||
| audited_url: "https://publisher.example.com".to_string(), | ||
| page_title: None, | ||
| js_asset_count: 1, | ||
|
|
@@ -677,6 +743,7 @@ mod tests { | |
| #[test] | ||
| fn extract_gtm_container_id_reads_query_parameter_urls() { | ||
| let artifact = AuditArtifact { | ||
| lockr_app_id: None, | ||
| audited_url: "https://publisher.example".to_string(), | ||
| page_title: None, | ||
| js_asset_count: 1, | ||
|
|
@@ -703,6 +770,7 @@ mod tests { | |
| #[test] | ||
| fn artifact_serialization_uses_expected_shape() { | ||
| let artifact = AuditArtifact { | ||
| lockr_app_id: None, | ||
| audited_url: "https://publisher.example".to_string(), | ||
| page_title: None, | ||
| js_asset_count: 1, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.