1pub mod api;
46pub mod billing;
47pub mod registry;
48pub mod security;
49pub mod webhooks;
50
51pub use api::{check_api_usage, ApiUsageStatus, DEPRECATED_KEYS, GRAPHQL_KEYS, REST_KEYS};
53pub use billing::{
54 check_billing_compliance, BillingStatus, SHOPIFY_BILLING_KEYS, THIRD_PARTY_PAYMENT_KEYS,
55};
56pub use registry::{PatternError, PatternMatch, PatternRegistry, PatternSet, Result};
57pub use security::{
58 find_security_issues, SecretMatch, SecurityIssues, DANGEROUS_FUNCTION_KEYS,
59 HARDCODED_SECRET_KEYS, HTTP_URL_KEYS,
60};
61pub use webhooks::{
62 check_gdpr_webhooks, GdprWebhookStatus, GDPR_WEBHOOK_KEYS, WEBHOOK_SECURITY_KEYS,
63};
64
65pub fn check_all_compliance(text: &str) -> ComplianceReport {
67 ComplianceReport {
68 api: check_api_usage(text),
69 security: find_security_issues(text),
70 webhooks: check_gdpr_webhooks(text),
71 billing: check_billing_compliance(text),
72 }
73}
74
75#[derive(Debug, Clone, Default)]
77pub struct ComplianceReport {
78 pub api: ApiUsageStatus,
80 pub security: SecurityIssues,
82 pub webhooks: GdprWebhookStatus,
84 pub billing: BillingStatus,
86}
87
88impl ComplianceReport {
89 pub fn is_fully_compliant(&self) -> bool {
91 self.api.is_compliant()
92 && !self.security.has_issues()
93 && self.webhooks.is_compliant()
94 && (self.billing.is_compliant() || !self.billing.has_billing())
95 }
96
97 pub fn overall_status(&self) -> &'static str {
99 if self.security.severity() == "critical" {
100 "critical"
101 } else if !self.api.is_compliant()
102 || !self.webhooks.is_compliant()
103 || (self.billing.has_billing() && !self.billing.is_compliant())
104 {
105 "fail"
106 } else if self.security.has_issues() {
107 "warning"
108 } else {
109 "pass"
110 }
111 }
112
113 pub fn summary(&self) -> Vec<String> {
115 let mut issues = Vec::new();
116
117 if self.api.uses_rest {
119 issues.push("REST API usage detected (should use GraphQL)".to_string());
120 }
121 for dep in &self.api.deprecated_endpoints {
122 issues.push(format!("Deprecated API: {}", dep));
123 }
124
125 for func in &self.security.dangerous_functions {
127 issues.push(format!("Dangerous function: {}", func));
128 }
129 for secret in &self.security.hardcoded_secrets {
130 issues.push(format!(
131 "Potential hardcoded secret: {} ({} occurrences)",
132 secret.pattern, secret.count
133 ));
134 }
135 if self.security.insecure_http {
136 issues.push("Insecure HTTP URLs detected".to_string());
137 }
138
139 for missing in self.webhooks.missing() {
141 issues.push(format!("Missing GDPR webhook: {}", missing));
142 }
143 if !self.webhooks.has_security() {
144 issues.push("HMAC verification not detected for webhooks".to_string());
145 }
146
147 for provider in &self.billing.third_party_detected {
149 issues.push(format!("Third-party payment provider: {}", provider));
150 }
151
152 issues
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn test_check_all_compliance() {
162 let compliant_code = r#"
163 // GraphQL API usage
164 const client = new AdminGraphqlClient();
165 const result = await client.query({
166 data: gql`query { products { edges { node { id } } } }`,
167 });
168
169 // GDPR webhooks
170 app.post('/webhooks/customers/data_request', handleDataRequest);
171 app.post('/webhooks/customers/redact', handleCustomerRedact);
172 app.post('/webhooks/shop/redact', handleShopRedact);
173
174 // HMAC verification
175 const hmac = req.headers['X-Shopify-Hmac-SHA256'];
176 verifyHmac(hmac, body);
177
178 // Shopify Billing
179 const subscription = await client.mutate({
180 mutation: appSubscriptionCreate,
181 variables: { name: "Pro Plan" }
182 });
183 "#;
184
185 let report = check_all_compliance(compliant_code);
186 assert!(report.api.uses_graphql);
187 assert!(report.webhooks.is_compliant());
188 assert!(report.webhooks.has_security());
189 assert!(report.billing.uses_shopify_billing);
190 assert!(!report.security.has_issues());
191 assert_eq!(report.overall_status(), "pass");
192 }
193
194 #[test]
195 fn test_non_compliant_code() {
196 let non_compliant = r#"
197 // REST API (deprecated)
198 fetch('/admin/api/2024-01/products.json');
199
200 // Stripe integration (not allowed)
201 const stripe = new Stripe(apiKey);
202
203 // Missing GDPR webhooks
204 // eval usage
205 const result = eval(userInput);
206 "#;
207
208 let report = check_all_compliance(non_compliant);
209 assert!(report.api.uses_rest);
210 assert!(!report.billing.third_party_detected.is_empty());
211 assert!(!report.webhooks.is_compliant());
212 assert!(report.security.has_issues());
213 assert!(!report.is_fully_compliant());
214 }
215
216 #[test]
217 fn test_summary() {
218 let code = r#"
219 fetch('/admin/api/2022-01/products.json');
220 element.innerHTML = userInput;
221 "#;
222
223 let report = check_all_compliance(code);
224 let summary = report.summary();
225
226 assert!(summary.iter().any(|s| s.contains("REST API")));
227 assert!(summary.iter().any(|s| s.contains("Deprecated API")));
228 assert!(summary.iter().any(|s| s.contains("inner_html")));
229 assert!(summary.iter().any(|s| s.contains("Missing GDPR webhook")));
230 }
231}